- 메모리 관리 배경
각각의 프로세스는 독립된 메모리 공간을 갖고, 운영체제 혹은 다른 프로세스의 메모리 공간에 접근할 수 없는 제한이 걸려 있음
(단, 운영체제만이 운영체제 메모리 영역과 사용자 메모리 영역의 접근에 제약 없음)
1) Swapping : 메모리 관리를 위해 사용되는 기법
표준 Swapping 방식으로는 Round-robin과 같은 스케줄링의 다중 프로그래밍 환경에서 CPU 할당 시간이 끝난 프로세스의 메모리를 보조 기억장치(하드디스크)로 내보내고 다른 프로세스의 메모리를 불러 들일 수 있음
(이 과정을 swap이라 함. 주기억장치(RAM)으로 불러오는 과정을 swap-in, 보조 기억장치로 내보내는 과정을 swap-out이라 함.
swap에는 큰 디스크 전송시간이 필요하기 때문에 현재에는 메모리 공간이 부족할 때 Swapping이 시작됨)
2) 단편화(Fragmentation) : 프로세스들이 메모리에 적재되고 제거되는 일이 반복되다 보면, 프로세스들이 차지하는 메모리 틈 사이에 사용하지 못할 만큼 작은 자유공간들이 늘어나는데, 이를 단편화라 함
- 외부단편화 : 메모리 공간 중 사용하지 못하게 되는 일부분, 물리 메모리(RAM)에서 사이사이 남는 공간들을 모두 합치면 충분한 공간이 되는 부분들이 분산되어 있을 때 발생한다고 볼 수 있음
- 내부단편화 : 프로세스가 사용하는 메모리 공간에 포함된 남는 부분
3) 압축 : 외부 단편화를 해소하기 위해 프로세스가 사용하는 공간들을 한쪽으로 몰아 자유공간을 확보하는 방법론(작업효율 좋지 않음)
4) 연속 할당 방식 : 각각의 프로세스를 물리적 메모리의 연속적 공간에 올리는 방식
- 고정 분할 방식 : 물리적 메모리를 주어진 개수만큼의 영구적인 분할로 미리 나누고 각 분할에 하나의 프로세스를 적재하여 실행시키는 방식(단편화 문제 발생)
- 가변 분할 방식 : 메모리에 적재되는 프로그램의 크기에 따라 분할의 크기, 개수가 동적으로 변하는 방식(외부 단편화 발생)
5) 불연속 할당 방식 : 하나의 프로세스를 물리적 메모리의 여러 영역에 분산하여 적재하는 방식
- 페이징 : 프로세스를 동일한 크기의 페이지로 나눔(내부 단편화 문제)
- 세그멘테이션 : 프로세스를 서로 다른 크기의 논리적 단위 세그멘테이션으로 나눔(외부 단편화 문제)
- Paging vs Segmentation
가상 메모리를 관리하는 기법
가상 메모리는 메모리에 로드된 즉, 실행 중인 프로세스가 가상의 공간을 창조하여 마치 커다란 물리 메모리를 갖고 있는 것처럼 사용할 수 있도록 하는 것
실제 메로리 주소가 아닌 가상 메모리 주소를 주는 방식
1) 페이징(Paging)
- 프로세스의 주소 공간을 동일한(고정된) 사이즈의 페이지 단위로 나누어 물리적 메모리에 불연속적으로 저장하는 방식
- 외부 단편화와 압축 작업을 해소하기 위함
- 메모리는 Frame이라는 고정 크기로 분할되고, 프로세스는 Page라 불리는 고정 크기로 분할됨
- (메모리는 프레임의 집합, 프로세스는 페이지의 집합)
- CPU는 논리 주소로 프로그램이 설정한대로 연속적인 주소값으로 명령을 내리고, 이는 메모리로 가기 전에 각 페이지의 실제 메모리 주소가 저장되어 있는 테이블에서 물리 주소로 변경되어야 함
- 프로세스를 정상적으로 사용하기 위해 MMU의 재배치 레지스터를 여러 개 사용해서 각 페이지의 실제 주소로 변경해줌
- 이러한 여러 개의 재배치 레지스터를 페이지 테이블(Page Table)이라 함
- 단점 : 내부 단편화 문제의 비중이 늘어남
Q. 하나의 프로세스는 연속적인 동작을 수행하는데, 이를 작은 조각으로 나누어 여기저기 흩어진다면 정상적으로 동작할까?
메모리상에서 여러 곳에 흩어진 프로세스를 수행하기 위해 MMU를 통해 논리 주소와 물리 주소를 나누어 사용함으로써 CPU를 속어야 함
실제 메모리는 전혀 연속적이지 않는데, CPU는 연속적으로 사용하고 있다는 것을 보장받으며 정상적으로 수행함
2) 세그멘테이션(Segmentation)
- 프로세스를 서로 크기가 다른 논리적 블록 단위인 세그먼트(Segment)로 분할하고 메모리에 배치하는 것을 말하며, 각 세그먼트의 크기는 일정하지 않음
- 프로세스를 Code + Data + Stack 영역으로 나눔(code, data, stack 각각 내부에서 더 작은 세그먼트로 나누기 가능)
- 세그먼트를 메모리에 할당할 때는 페이지를 할당하는 것과 동일
- 하지만 테이블은 조금 다름. 세그먼테이션을 위한 테이블은 세그먼트 테이블이라고 함
- 세그먼트 테이블은 세그먼트 번호와 시작 주소, 세그먼트 크기를 엔트리로 가짐
- 세그먼트에서 주소변환 역시 페이징과 유사함
- 주의할 점은 세그먼트의 크기는 일정하지 않기 때문에 테이블에 limit 정보가 주어짐
- 그리고 CPU에서 해당 세그먼트의 크기를 넘어서는 주소가 들어오면 인터럽트가 발생하여 해당 프로세스를 강제로 종료 시킴
- 단점 : 외부 단편화 문제 발생 가능
- 보호와 공유
보호(Protection) : 모든 주소는 페이지 테이블을 경유하므로 테이블을 이용해서 보호 기능을 수행할 수 있음
대표적으로 페이지마다 r(read), w(write), x(execute) 비트를 두어 해당 비트가 켜져있을 때, 그 수행히 가능하도록 함
공유(Sharing) : 메모리 낭비를 방지하기 위함이 목적
같은 프로그램을 쓰는 복수 개의 프로세스가 있다면 프로세스의 메모리는 code + data + stack 영역으로 나뉘는데 프로그램이 같다면 code 영역은 같음 -> 그러므로 하나의 code 영역을 복수 개의 프로세스가 공유하여 메모리 낭비를 줄임
(단, code가 공유되려면 code가 변하지 않는 프로그램이어야 함 -> non-self-modifying cod = reentrant code = puer code)
세그멘테이션에서 보호와 공유 : 페이징보다 세그멘테이션에서의 보호와 공유는 더 효율적임
보호에서는 세그멘테이션 역시 r,w,x 비트를 테이블에 추가하는데 세그멘테이션은 논리적으로 나누기 때문에 해당 비트를 설정하기 매우 간단함
페이징은 code + data + stack 영역이 있을 때, 이를 일정한 크기로 나누므로 두 가지 영역이 섞일 수 있어 비트 설정이 까다로움
공유에서도 마찬가지로 페이징에서는 code 영역을 나눠도 다른 영역이 포함될 확률이 높지만, 세그멘테이션은 정확히 code 영역만을 나누기 때문에 더 효율적으로 공유를 수행할 수 있음
- 세그멘테이션과 페이징
세그멘테이션은 페이징과 유사하고 보호와 공유 측면에서는 더 나은 성능을 보여주지만 현재 대부분은 페이징 기법을 사용함
그 이유는 세그멘테이션에는 치명적인 단점이 존재하기 때문
-> 메모리 할당을 처음할 때, 다중 프로그래밍에서의 문제는 크기가 서로 다른 프로세스로 인해 여러 크기의 hole이 발생함
이로 인해 어느 hole에 프로세스를 할당하는 것에 대한 최적화 알고리즘이 존재하지 않고, 외부 단편화로 인해 메모리 낭비가 큼
세그멘테이션도 다양한 세그먼트 크기로 나누기 때문에 다양한 크기의 hole이 발생함
결론적으로 세그멘테이션은 보호와 공유에서 효율적이고, 페이징은 외부 단편화 문제를 해결할 수 있음
그러므로 이 두가지를 합쳐서 사용하는 방법이 나옴 -> 세그먼트를 페이징 기법으로 나누는 것(Paged Segmentation)
그러나 세그먼트와 페이지가 동시에 존재하기 때문에 주소 변환을 두 번 해야하는 단점이 발생
(즉, CPU에서 세그먼트 테이블에서 주소 변환을 하고, 그 다음 페이지 테이블에서 또 주소 변환을 해야함)