10. Virtual Memory

실행 전 프로세스 전체가 메모리에 올라갈 필요가 없게 하기 위해서 가상 메모리를 사용할 수 있다. 이는 물리적 메모리보다 큰 프로그램을 가능케 한다. 또한, 가상 메모리는 메인 메모리를 대단히 큰 저장소 배열로 추상화시킴으로써 물리적 메모리와 메인 메모리를 분리시키고 프로그래머가 메모리 저장소 한계를 고려할 필요가 없게 한다. 또한, 파일과 라이브러리들의 프로세스간 공유를 가능케 하고 공유 메모리를 구현하게 하기도 한다. 또한, 프로세스 생성에 대한 효율적인 메커니즘도 제공한다. 이는 구현이 쉽지 않으며, 무신경하게 쓰였을 경우 성능을 떨어트리기도 한다. 이 장에서는 가상 메모리에 대해 다룬다.

10.1. Background

명령문이 실행되기 위해서는 메모리에 로드되어야 하기 때문에 메모리 관리 방법은 중요하다. 하나의 접근법은 전체 논리적 주소 공간을 물리적 메모리에 로드하는 것이지만, 프로그램의 크기가 물리적 메모리에 제한된다. 또한, 전체 프로그램을 로드하는 건 대개 불필요하다. 전체 프로그램이 로드되어야 하는 경우더라도 대개 전체 프로그램을 꼭 동시에 로드해야 할 필요는 없다. 그래서 실행할 프로그램의 일부만을 물리적 메모리에 로드할 수 있다면 좋을 것이다.

가상 메모리는 논리적 메모리를 물리적 메모리와 분리시켜 메모리 제한에 신경쓸 필요가 없게 한다. 프로세스의 가상 메모리 주소는 프로세스가 메모리에 어떻게 저장되는지에 대한 논리적 구조로, 이는 연속적으로 이루어져 있다. 실제 물리적 메모리는 페이지로 이루어져 있으며 이 페이지들은 연속적일 필요가 없다는 것을 상기하자. 힙은 위쪽으로, 스택은 아래쪽으로 커져 나가는데, 이 사이 구멍은 가상 주소 공간이지만 힙이나 스택 영역이 커질 때에만 실제 물리적 페이지를 요구한다. 이러한 구멍들을 포함하는 가상 주소 공간을 희소 주소 공간이라 한다. 힙과 스택 영역이 커짐에 따라 이를 채울 수 있으므로 이러한 방법이 유용하다.

또한, 가상 메모리는 물리적 메모리와 논리적 메모리를 분리하는 것 말고도 파일과 메모리를 페이지 공유를 통해 2개 이상의 프로세스간 공유를 시킬 수 있다. C 표준 라이브러리와 같은 시스템 라이브러리를 여러 프로세스간 공유시킬 수도 있고, 프로세스간 메모리를 공유시켜서 통신을 시킬 수도 있고, 프로세스 생성 시 페이지를 공유시켜 프로세스 생성을 빠르게 할 수도 있다.

10.2. Demand Paging

프로그램이 메모리에 로딩될 때에는 필요한 경우에만 페이지를 로딩하는 페이징 요구의 방법이 나으며 이것이 가상 메모리 시스템에서 보편적으로 사용된다.

10.2.1. Basic Concepts

이를 구현하기 위해서는 메모리에 있는 페이지와 이차 저장소에 있는 페이지를 구별해야 한다. 유효-무효 비트로 이를 구현할 수 있으며, 무효 비트가 마킹된 페이지에 접근했을 때 운영 체제 트랩이 발생하는 것을 페이지 폴트라 한다. 이는 다음과 같은 과정으로 이루어진다.

  1. 프로세스에 대한 내부 테이블을 참조해 페이지의 유효성을 판단한다.
  2. 페이지 참조가 무효하면 프로세스를 종료시킨다. 참조가 유효하나 페이지가 로딩되지 않은 경우라면 페이지를 로드한다.
  3. 자유 프레임을 찾는다.
  4. 이차 저장소에 대해 해당 페이지를 새로이 할당된 프레임으로 읽어들이는 연산을 스케쥴링한다.
  5. 저장소 읽기가 끝나면 프로세스에 대한 내부 테이블과 페이지 테이블을 그에 맞게 수정한다.
  6. 트랩에 의해 중단된 명령어부터 프로세스를 시작한다.

메모리 내 페이지가 없는 경우부터 프로세스가 시작될 수도 있다 (순수 페이징 요구). 이론적으로 한 명령어가 여러 페이지 폴트를 일으킬 수도 있는데, 프로그램은 대개 참조 인접성을 갖기 때문에 이런 경우는 거의 없다. 하드웨어가 페이징 요구를 지원하기 위해서는 페이지 테이블에 유효성을 기록하고 이차 저장소를 교환 공간으로 활용한다. 페이징 요구의 핵심 조건은 페이지 폴트 이후에 임의의 명령어로부터 프로세스를 재개할 수 있는 것이다. 이 때 한 명령어가 여러 다른 장소를 수정할 경우 문제가 된다. 이는 수정되는 장소의 경계값을 이용해 페이지 폴트를 미리 일으킴으로써 방지하거나, 덮어씌워진 장소의 값들을 임시 레지스터에 저장함으로써 해결할 수 있다. 페이징은 모든 시스템에 추가될 수 있는 것은 아니고 페이지 폴트가 치명적 오류를 낳는 비 페이징 요구 환경에 대해서만 추가될 수 있다.

10.2.2. Free-Frame List

운영 체제는 페이지 폴트에 대응하기 위해 자유 프레임 리스트를 구성해 놓는다. 이 프레임들은 필요에 따라 0으로 채워진다. 이 리스트 수가 일정 이하로 줄어들 경우 재구성해야 한다.

10.2.3. Performance of Demand Paging

페이징 요구는 시스템의 성능에 영향을 크게 미칠 수 있다. 이는 페이지 폴트 확률이 p, 메모리 접근 시간이 ma, 페이지 폴트 시간이 t일 때 유효 접근 시간(1 - p) \times ma + p \times t가 된다. 이를 계산하려면 페이지 폴트 시간을 알아야 하는데, 페이지 폴트 과정은 다음과 같다.

  1. 운영 체제에서 트랩을 생성한다.
  2. 레지스터와 프로세스 상태를 저장한다.
  3. 인터럽트가 페이지 폴트였는지를 판별한다.
  4. 페이지 참조가 유효한지 판단하고, 이차 저장소 내 페이지의 장소를 판정한다.
  5. 저장소에서 자유 프레임으로 불러온다.
  6. 대기 중 CPU 코어를 다른 프로세스에 할당한다.
  7. 저장소 I/O 부분 시스템으로부터 인터럽트를 받는다.
  8. CPU 코어가 할당된 다른 프로세스의 레지스터와 프로세스 상태를 저장한다.
  9. 인터럽트가 이차 저장소 기기로부터 발생했는지를 판별한다.
  10. 페이지 테이블과 다른 테이블의 내용을 업데이트한다.
  11. CPU 코어가 이 프로세스에 재할당되기를 기다린다.
  12. 레지스터, 페이지 테이블, 프로세스 상태를 복구하고 인터럽트된 명령어를 재개한다.

요약하면 다음과 같다.

  1. 페이지 폴트 인터럽트를 수행한다.
  2. 페이지를 읽어온다.
  3. 프로세스를 재시작한다.

첫 번째와 세 번째 작업의 경우 구현에 따라 수행 시간을 대폭 감소시킬 수 있다. 페이지 폴트 시간은 메모리 접근 시간보다 훨씬 크므로, 유효 접근 시간은 페이지 폴트 비율에 의존한다.

페이징 요구의 또 다른 관점은 교환 공간의 사용이다. 교환 공간의 입출력은 파일 시스템보다 빠르기 때문이다. 시스템에서 페이징 처리량을 늘리는 하나의 방법은 파일 전체를 프로그램 시작 시점에 교환 공간에 복사하는 것이지만, 또 다른 접근법은 페이지가 대체될 때 이들을 교환 공간에 쓰는 것이다. 페이징 요구시 교환 공간의 크기가 제한된 경우 파일 시스템이 일종의 백업 저장소 역할을 하기도 한다. 그러나 이럴 때도 파일과 관련 없는 페이지를 담아두기 위해 교환 공간은 필요하다 (익명 메모리). 모바일 운영 체제는 대개 페이지 교환을 지원하지 않는다.

10.3. Copy-on-Write

fork() 시스템 호출을 이용해 생성되는 프로세스는 페이징 요구에 대한 필요성을 최초에 우회할 수 있다. 또한, 자식 프로세스는 최초에는 부모 프로세스와 페이지를 공유하고 쓰기 시에만 복사를 할 수도 있다. UNIX의 여러 버전들은 vfork()라는 가상 메모리 포크를 지원한다. 이는 쓰기 시에만 복사도 지원하지 않으며, 자식 프로세스가 부모 프로세스의 주소 공간을 이용하고 부모 프로세스는 지연된다.

10.4. Page Replacement

각 페이지에 대한 페이지 폴트가 꼭 한 번으로 제한되는 것은 아니다. 멀티프로그래밍의 정도를 높이면 메모리를 과할당하게 되기 때문이다. 이 경우 페이지 폴트가 일어날 때 자유 프레임이 없을 수 있는데, 운영 체제는 프로세스를 종료할 수도 있고, 교환을 통해 다른 프로세스의 페이지를 교환 공간에 배치해 멀티프로그래밍의 정도를 낮출 수도 있다. 이 때 표준 교환은 좋은 방법이 아닌데, 대다수의 운영 체제는 페이지 치환을 사용한다.

10.4.1. Basic Page Replacement

페이지 치환은 다음과 같다. 자유 프레임이 없을 때, 사용되지 않는 프레임을 찾아 그 내용물을 교환 공간에 쓰고 페이지 테이블을 변경함으로써 자유롭게 만든다. 이제 이 새로운 자유 프레임이 페이지 폴트를 일으킨 페이지를 저장하게 한다. 이 때 페이지 폴트는 다음과 같이 바뀐다:

  1. 이차 저장소 내 해당 페이지의 위치를 찾는다.
  2. 자유 프레임을 찾는다. 없다면 페이지 치환 알고리즘으로 피해 프레임을 선택해 이를 이차 저장소에 쓴다.
  3. 해당 페이지를 새 자유 프레임에 쓰고 페이지 테이블과 프레임 테이블을 변경한다.
  4. 페이지 폴트가 발생한 지점부터 프로세스를 재개한다.

자유 프레임이 없다면 페이지 전환이 2회 필요하므로 오버헤드도 커진다. 이는 수정 비트(불결 비트)를 도입해서 줄일 수 있다. 이는 페이지가 이차 저장소에서 읽어들여진 이후 수정되었는지를 체크한다. 수정된 적이 없다면 메모리 페이지를 저장소에 쓸 필요가 없으므로 I/O 시간을 절반으로 줄일 수 있다.

페이지 치환은 페이징 요구에 있어 기본이 되며, 논리적 메모리와 물리적 메모리를 분리시키고, 논리적 주소 공간의 크기를 물리적 메모리에 제한받지 않게 한다. 이 때 두 가지 알고리즘이 필요한데, 프레임 할당 알고리즘페이지 치환 알고리즘이다. 즉, 각 프로세스에 얼마나 많은 프레임을 할당해야 할지를 결정해야 하고, 페이지 치환이 필요할 때 치환할 프레임을 결정해야 한다. 이 알고리즘은 메모리 참조에 대한 참조 문자열에 대해 알고리즘을 수행해서 그로부터 계산된 페이지 폴트 비율로 평가한다.

10.4.2. FIFO Page Replacement

가장 간단한 페이지 치환 알고리즘은 선입선출 알고리즘이다. 이는 각 페이지를 메모리에 로드된 시점과 연관시켜서 페이지가 치환될 때 가장 오래된 페이지를 택한다. 그러나 이는 비효율적인데, 가끔은 더 많은 프레임에 대한 페이지 폴트가 더 적은 프레임의 경우보다 더 많아지기도 한다(벨라디 이례).

10.4.3. Optimal Page Replacement

최적의 페이지 치환 알고리즘은 다음과 같다: 최장 시간동안 사용되지 않을 페이지를 교체하는 것. 하지만 미래의 페이지 사용을 예측해야 하므로 구현이 어렵다.

10.4.4. LRU Page Replacement

최적의 페이지 치환 알고리즘을 근사하기 위해서 최장 시간동안 사용되지 않은 페이지를 교체하는 접근이 사용된다. 이를 최소 최근 사용 알고리즘이라 한다. 문제는 이를 구현하는 데 있어 하드웨어의 큰 지원이 필요하다는 것이다. 이는 카운터로 구현할 수도 있고 스택으로 구현할 수도 있다. 최적의 페이지 치환 알고리즘과 같이, 이는 스택 알고리즘에 속하므로 벨라디 이례를 겪지 않는다. 카운터로 구현하든 스택으로 구현하든 표준 TLB 레지스터 이상으로 하드웨어의 큰 지원이 필요하다. 메모리 참조 각각에 대해 이를 업데이트해야 하기 때문이다.

10.4.5. LRU-Approximation Page Replacement

오버헤드를 감당할 수 없는 시스템에서는 참조 비트를 둬서 최소 최근 사용 알고리즘을 근사하기도 한다.

10.4.5.1. Additional-Reference-Bits Algorithm

추가 참조 비트를 특정한 시간마다 기록함으로써 참조 횟수의 순서를 근사할 수도 있다. 변환 레지스터에 기록되는 기록 비트의 수가 0일 수도 있는데, 이를 이차 기회 페이지 치환 알고리즘이라 한다.

10.4.5.2. Second-Chance Algorithm

이차 기회 페이지 치환 알고리즘은 기본적으로 선입 선출 알고리즘이다. 참조 비트가 0이면 이 페이지를 치환하고 참조 비트가 1이면 두 번째 기회를 주고 새로운 선입 선출 페이지를 찾는다. 즉, 두 번째 기회가 주어진 페이지는 다른 페이지들 전부가 치환되기 전까지는 치환되지 않는다. 이러한 시계 알고리즘은 원환 큐로 구현할 수 있다. 모든 비트가 세팅된 상태라면 이차 기회 페이지 치환 알고리즘은 선입 선출 알고리즘과 다를 바 없다.

10.4.5.3. Enhanced Second-Chance Algorithm

참조 비트와 수정 비트를 순서쌍으로 둬서 이차 기회 페이지 치환 알고리즘을 보정할 수 있다.

10.4.6. Counting-Based Page Replacement

참조 횟수 기반 페이지 치환 알고리즘을 알아보자.

  • 최소 사용 빈도(LRU) 페이지 치환 알고리즘은 사용 횟수가 최소인 페이지가 치환된다. 이는 최초에 많이 사용된 페이지를 치환하지 않는다는 문제점이 있는데, 사용 횟수를 시간에 따라 지수적으로 감소하게 하면 된다.
  • 최대 사용 빈도(MFU) 페이지 치환 알고리즘은 사용 횟수가 최대인 페이지가 치환된다.

이 중 어느 것도 자주 쓰이지 않는다.

10.4.7. Page-Buffering Algorithms

수정된 페이지의 목록을 유지한 채로, 페이징 기기가 휴식 중일 때 수정된 페이지들을 이차 저장소에 쓸 수도 있다. 아니면 자유 프레임 목록을 유지하되 어떤 페이지가 각 프레임에 들어갔는지를 기억할 수도 있다.

10.4.8. Applications and Page Replacement

일부 경우에는 운영 체제의 가상 메모리를 통해 데이터를 접근하는 애플리케이션의 성능이 버퍼링을 제공하지 않는 운영 체제보다 성능이 더 나쁘기도 하다. 운영 체제는 일반적인 알고리즘을 제공하기 때문이다. 그래서 어떤 운영 체제들은 파일 시스템 자료구조 없이 이차 저장소 파티션을 논리적 블록의 큰 배열인 원시 디스크로 제공하기도 한다.

10.5. Allocation of Frames

이제 프레임 할당에 대해 알아보자. 기본적인 전략은 사용자 프로세스에 자유 프레임을 할당한다는 것이다.

10.5.1. Minimum Numbe of Frames

할당해야 할 프레임의 최소 숫자에 대해 알아보자. 프로세스에 할당된 프레임의 수가 적어질 수록 페이지 폴트 수는 늘어난다. 그러므로 충분한 프레임 수를 할당해야 한다. 할당해야 할 프레임의 최소 숫자는 컴퓨터 아키텍쳐에 의해 정의된다. 최대 숫자는 가용 물리적 메모리의 양에 의해 정의된다.

10.5.2. Allocation Algorithms

m 프레임을 n개의 프로세스에 분할하는 가장 쉬운 방법은 모든 프로세스에 똑같이 할당하는 동일 할당이다. 하지만 프로세스마다 메모리 요구량이 다르기 때문에, 이를 해결하는 비례 할당을 하기도 한다. 두 할당 모두 멀티프로그래밍의 정도에 따라 구체적인 할당이 달라지며, 두 할당 모두 높은 우선도의 프로세스가 낮은 우선도의 프로세스와 같은 대접을 받는다. 비례 할당에서 우선도를 감안하도록 해서 이를 해결할 수 있다.

10.5.3. Global versus Local Allocation

프레임 할당에 대한 중요한 요소는 페이지 치환이다. 이는 두 개의 넓은 카테고리로 나뉠 수 있다: 전역 치환국소적 치환. 전역 치환은 모든 프레임의 집합으로부터 프로세스가 치환 프레임을 선택한다. 국소적 치환은 할당된 프레임에 대한 자체적 집합으로부터 프로세스가 치환 프레임을 선택한다. 전역 치환의 문제점은 프로세스가 선택할 치환 프레임의 집합이 다른 프로세스에도 의존한다는 것이다. 그러므로 같은 프로세스가 외부 환경에 따라 성능이 달라질 수 있다. 국소적 치환의 경우엔 그렇지 않으나, 시스템 처리량이 더 적다. 그래서 전역 치환이 더 널리 쓰인다.

전역 치환을 구현할 때에는, 자유 리스트의 수가 0이 될 때까지 기다리는 것보다 특정 기준치 이하가 되면 커널의 수확자 루틴을 가동해 페이지 치환을 하는 것이 낫다. 이는 언제나 충분한 가용 메모리가 있음을 보장한다. 가용 메모리가 아주 낮아진다면 메모리 부족 (OOM) 킬러 가 프로세스 중 하나를 선택해 종료시켜 메모리를 해제시킨다.

10.5.4. Non-Uniform Memory Access

비균일 메모리 접근(NUMA)시스템에서는 모든 메모리가 균일하게 접근되지는 않는다. 이 경우엔 어떤 CPU가 메인 메모리의 어떤 부분은 더 빨리 접근하고 어떤 부분은 더 느리게 접근한다. 이런 경우 어떤 페이지 프레임이 어떤 장소에 저장되어 있는지를 관리하는 것은 성능에 크게 영향을 준다. 이를 관리하려면 스케쥴러가 프로세스가 마지막으로 동작한 CPU들을 알고 있어야 한다. 여러 스레드가 추가될 경우 이는 좀 더 복잡해진다. Linux의 경우 스케쥴링 영역에 대한 계층화를 통해 이를 타개한다. Solaris의 경우 지역 그룹(lgroup)화를 통해 이를 해결한다.

10.6. Thrashing

프레임이 부족한 프로세스의 경우 페이지 교환을 끝없이 하는 스래싱을 하게 된다. 이는 성능에 큰 영향을 미친다.

10.6.1. Cause of Thrashing

스래싱은 CPU 활용의 감소로 인한 스케쥴러의 멀티프로그래밍 정도 증가에 의해 발생한다. 이를 해결하려면 멀티프로그래밍의 정도를 감소시켜야 한다. 스래싱의 효과를 제한하기 위해서는 국소적 치환 알고리즘 (또는 우선도 치환 알고리즘)을 사용할 수 있지만, 유효 접근 시간이 증가할 수 있다. 결국 스래싱을 막기 위해서는 프로세스가 필요한 만큼의 프레임을 제공해 줘야 한다. 이는 프로세스 실행의 국소 모델을 정의한다. 이 때 필요한 국소성은 프로그램의 구조와 자료 구조에 의해 정의된다.

10.6.2. Working-Set Model

동작 집합 모델은 국소성 가정을 기반으로 한다. 이는 동작 집합 윈도우를 정의하는 매개변수를 사용한다. 가장 최근의 동작 집합 윈도우 내 페이지들을 동작 집합이라 한다. 이는 국소성에 대한 근사라 할 수 있다. 동작 집합의 가장 중요한 특성은 그 크기다. 동작 집합의 크기의 총합이 가용 프레임의 수보다 크면 스래싱이 일어나므로, 이 때에는 운영 체제가 프로세스를 선택해 지연시킨 뒤 그 페이지들을 교환 저장소에 쓴다. 난점은 동작 집합을 추적하는 것인데, 이는 고정 길이 타이머 인터럽트와 참조 비트로 근사할 수 있다. 이는 근사치이지 정확한 추정은 아니다. 이 근사의 정확성은 기록 비트와 인터럽트 빈도를 늘려서 보정할 수 있다. 물론 이러면 오버헤드가 커진다.

10.6.4. Page-Fault Frequency

페이지 폴트 빈도(PFF)를 사용하는 전략은 더 직접적이다. 이는 페이지 폴트 빈도가 특정 값을 넘어서면 프로세스에 또 다른 프레임을 할당한다. 특정 값 이하로 떨어지면 프로세스에서 프레임을 회수한다. 프로세스의 페이지 교환도 연관된다.

10.6.4. Current Practice

가장 나은 방법은 물리적 메모리를 충분히 확보하는 것이다.

10.7. Memory Compression

페이징에 대한 대안은 메모리 압축으로, 여러 프레임을 단일 프레임으로 압축해 페이지 교환 없이 메모리 사용을 줄이는 것이다. 이는 페이지 교환을 지원하지 않는 모바일 시스템에 있어 핵심적이다. 추가로, Windows 10에서는 전역 윈도우 플랫폼(UWP) 아키텍쳐를 도입해 모바일 기기에서 메모리 압축에 대한 대상이 될 수 있게 하였다.

메모리 압축은 압축 프레임을 저장하기 위해 자유 프레임을 할당하게 하긴 하지만, 메모리 절약은 크다. 이 때 압축율과 압축 속도간의 트레이드오프가 존재한다.

10.8. Allocating Kernel Memory

사용자 모드 프로세스에 커널 메모리가 할당되기도 한다. 이유는 2가지인데, 커널 메모리는 페이지 크기보다 더 작은 메모리를 요구하기도 하고, 어떤 사용자 모드 프로세스는 반드시 연속된 페이지의 메모리가 필요하기 때문이다.

10.8.1. Buddy System

버디 시스템은 물리적으로 연속적인 페이지로 이루어진 고정 크기 세그먼트로부터 메모리를 할당한다. 이는 2의 승수 할당자로부터 메모리를 버디로 분할해 할당한다. 이의 이점은 버디끼리 합치기가 용이하다는 점이다. 단점은 파편화가 심해질 수 있다는 점이다.

10.8.2. Slab Allocation

두 번째 전략은 조각 할당으로, 물리적으로 연속된 페이지의 조각으로 이루어진 캐시를 가진다. 각 캐시에는 캐시가 표현하는 커널 자료 구조를 인스턴스화하는 오브젝트가 들어 있다. 조각 할당 알고리즘은 캐시를 사용해 커널 오브젝트를 저장한다. 조각 할당은 두 가지 이점이 있다. 파편화되는 메모리가 없고 메모리 요청이 빨리 만족될 수 있다는 점이다.

10.9. Other Considerations

다른 고려사항을 알아보자.

10.9.1. Prepaging

프로세스 시작 시 순수 페이징 요구의 많은 페이지 폴트를 막기 위해 사전 페이징을 하고는 한다. 이는 페이지 폴트 처리보다 비용이 더 싼 경우가 종종 있다. 하지만 어떤 페이지들이 들어가야 하는지를 미리 판단하기 어렵다는 점이 문제다.

10.9.2. Page Size

최적의 페이지 크기는 어떻게 구하나? 메모리를 최대한 활용하려면 페이지 크기가 작은 것이 좋다. 하지만, 이러면 입출력 시간이 늘어난다. 하지만 국소성이 높아지므로 전체 입출력 자체는 줄어들기도 한다. 또한, 해상도가 더 높아지기도 한다. 하지만 페이지 크기가 작아지면 페이지 폴트의오버헤드는 커진다. 그래서 정답은 없다.

10.9.3. TLB Reach

TLB에 대한 히트율과 비슷하게, 도달율을 논할 수 있다. 이는 TLB로부터 도달 가능한 메모리의 양을 나타낸다. 이는 페이지 크기를 늘리거나 복수의 페이지 크기를 제공해서 증진시킬 수 있다. 이 때문에 Linux는 대형 페이지를 제공하기도 한다. ARMv8 내의 TLB 엔트리는 추가로 연속 비트를 포함하기도 한다. 이 비트가 포함되어 있으면 해당 TLB 엔트리는 연속된 메모리 블록을 매핑한다.

10.9.4. Inverted Page Tables

반전 페이지 테이블은 가상 메모리에서 물리적 메모리의 전환을 추적하기 위해 필요한 물리적 메모리의 양을 줄이는 데 그 목적이 있다. 하지만 반전 페이지 테이블의 경우 프로세스의 논리적 주소 공간을 모두 담지는 않는다. 페이지 폴트가 일어날 때에는 이러한 정보가 필요한데, 이를 위해 외부 주소 테이블이 존재해야 한다. 이러한 외부 주소 테이블의 존재가 반전 페이지 테이블의 이점을 상쇄시키지는 않는다.

10.9.5. Program Structure

사용자가 페이징 요구를 알고 프로그래밍한다면 시스템 성능이 더 빨라질 수 있다. 자료 구조의 선택도 중요하다. 스택 같은 경우 국소성이 크고, 해시 테이블 같은 경우 국소성이 나쁘다. 컴파일러와 로더 또한 페이징에 큰 효과를 미친다.

10.9.6. I/O Interlock and Page Locking

페이징 요구가 사용될 때, 어떨 때는 페이지 일부가 메모리에 잠금될 것을 요구하기도 한다. 다음의 상황을 고려해 보자: 프로세스가 I/O 요청을 했는데 CPU가 다른 프로세스에 배당되었을 때 페이지 폴트가 일어나 이전 프로세스가 I/O 요청을 한 페이지를 가져가 버리는 상황. 이를 막기 위해서는 I/O가 사용자 메모리에서는 절대 일어나지 않게 하거나, 페이지가 메모리에 잠금되도록 하는 두 가지 해법이 있다. 이를 위해선 잠금 비트가 쓰인다. 이러한 페이지 고정은 I/O 외에도 일반적인 페이지 치환에 대해서도 자주 쓰인다. 잠금 비트는 버그가 날 경우 치명적이다.

10.10. Operating-System Examples

여러 운영 체제의 사례를 알아보자.

10.10.1. Linux

Linux는 페이징 요구에 추가로 LRU 근사 시계 알고리즘과 비슷한 전역 페이지 치환을 사용한다.

10.10.2. Windows

Windows는 페이징 요구를 사용한 가상 메모리를 클러스터링으로 구현한다. Windows의 가상 메모리 관리 핵심은 동작 집합 최소동작 집합 최대로 특정되는 동작 집합 제한값이다. 이는 프로세스가 강한 동작 집합 한계값을 따로 갖지 않는 이상 무시되지 않는다. Windows는 LRU 근사 시계 알고리즘을 사용한다. 가용 메모리의 양이 특정 기준 이하로 내려가면 가상 메모리 관리자는 자동 동작 집합 간소화 알고리즘을 써서 이를 복원한다.

10.10.3. Solaris

Solaris는 페이징간 기준치인 lotsfree 변수를 두고 자유 페이지의 수가 이것 이하로 내려가면 페이지 아웃이 발생한다. 이 때 쓰이는 페이지 스캔 알고리즘은 프로세스에 할당된 페이지와 데이터 파일에 할당된 페이지를 구분한다(우선도 페이징). Solaris도 LRU 근사 시계 알고리즘을 사용한다.

10.11. Summary

  • 가상 메모리는 물리적 메모리를 매우 큰 균일한 저장소의 배열로 추상화한다.
  • 가상 메모리의 이점은 다음 등이 있다: 1) 프로그램이 물리적 메모리보다 클 수 있다. 2) 프로그램 전체가 메모리에 로드될 필요가 없다. 3) 프로세스가 메모리를 공유할 수 있다. 4) 프로세스가 더 효율적으로 생성될 수 있다.
  • 페이징 요구는 페이지가 프로그램 실행 중 요구될 때만 로드되는 기술이다. 필요하지 않은 페이지는 메모리에 로드되지 않는다.
  • 메모리에 현재 없는 페이지가 접근될 때는 페이지 폴트가 일어난다. 이 때 페이지는 백업 저장소로부터 메모리 내 페이지 프레임으로 불러와져야 한다.
  • 쓸 때 복사하는 것은 자식 프로세스가 부모 프로세스와 같은 주소 공간을 공유하는 것을 허용한다. 자식 프로세스나 부모 프로세스가 페이지를 수정하면 페이지의 복사가 일어난다.
  • 가용 메모리가 적을 때에는 페이지 치환 알고리즘이 메모리 내 페이지를새 페이지로 선택해 치환한다. 페이지 치환 알고리즘은 선입선출, 최적, 최소 최근 사용 등이 있다. 순수 최소 최근 사용 알고리즘은 비실용적이므로, 대다수의 시스템은 최소 최근 사용 알고리즘의 근사를 사용한다.
  • 전역 페이지 치환 알고리즘은 치환 대상 페이지를 시스템 내 어느 프로세스에 대해서라도 선택한다. 국소 페이지 치환 알고리즘은 폴트를 일으킨 프로세스에 대해서만 페이지를 선택한다.
  • 스래싱은 시스템이 실행 시간보다 페이징 시간보다 더 많은 시간을 쏟을 때 발생한다.
  • 국소성은 같이 사용되는 페이지의 집합을 나타낸다. 프로세스가 실행될 때, 이는 국소성에서 국소성으로 이동한다. 동적 집합은 국소성에 기반하였으며 프로세스 내 현재 사용 중인 페이지의 집합들로 정의된다.
  • 메모리 압축은 여러 페이지들을 단일 페이지로 압축하는 메모리 관리 기술이다. 압축된 메모리는 페이징의 대안으로서 페이징을 지원하지 않는 모바일 시스템에서 쓰인다.
  • 커널 메모리는 사용자 모드 프로세스와 다르게 할당된다. 이는 가변 크기의 연속된 덩어리로 할당된다. 커널 메모리를 할당하는 두 보편적 방법은 1) 버디 시스템과 2) 조각 할당이다.
  • TLB 도달은 TLB로부터 접근 가능한 메모리의 양을 나타내며 TLB 내 엔트리의 크기에 페이지 크기를 곱한 것과 같다. 이를 늘리는 방법은 페이지 크기를 늘리는 것이다.
  • Linux, Windows, Solaris는 가상 메모리를 비슷하게 다루며, 페이징 요구와 쓰기 시 복사, 및 다른 특성들도 쓴다. 이들은 LRU 근사 시계 알고리즘의 변종을 쓴다.

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Google photo

Google의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

%s에 연결하는 중