
본 글의 내용은 Operating Systems: Three Easy Pieces의 Beyond Physical Memory: Mechanisms 챕터를 정리한 것입니다.
OS는 동시에 실행되는 많은 대규모 주소 공간을 지원해야 한다.
이를 위해 메모리 계층 구조에 새로운 수준의 계층이 필요하며, 큰 주소 공간을 지원하려면 사용량이 많지 않은 주소 공간의 일부를 숨길 수 있는 공간이 필요하다.
대체로 이런 공간은 메모리보다 더 큰 용량을 가져야 하므로 속도가 느리고, 현대 시스템에서 하드 디스크가 이 역할을 수행한다.
그럼 왜 더 큰 주소 공간을 지원해야 할까? 답은 편리함과 사용 편의성을 위해서이다.
주소 공간이 크면 메모리가 충분한지 걱정할 필요 없이 프로그램을 작성할 수 있다. 구형 시스템에서는 이것이 불가능하여 프로그래머가 필요할 때마다 코드나 데이터를 메모리 안팎으로 수동으로 이동해야 했다.
이렇게 교체 공간(swap space)을 추가하는 것으로 OS는 동시에 실행되는 프로세스들을 위한 대형 가상 메모리라는 환상을 만들어낼 수 있다. 초창기의 컴퓨터는 모든 페이지를 한 번에 저장할 수는 없었기 때문에 페이지를 교체하는 기능이 필수적으로 요구되었고, 이런 멀티프로그래밍의 등장과 편의성 지향의 콜라보로 실제 물리 메모리보다 더 많은 메모리 사용을 지원하고자 하게 되었다.

페이지를 이동하기 위해 디스크에 약간의 공간이 확보되어야 한다.
OS는 메모리에서 이 공간으로 페이지를 스왑하고, 이 공간에서 메모리로 페이
지를 스왑하기 때문에 이런 공간을 swap space라고 한다.
이 작업을 하기 위해서는 OS가 페이지의 디스크 주소를 기억해야 한다.
교체 공간의 크기는 중요하다고 할 수 있는데, 주어진 시간동안 사용할 수 있는 최대 메모리 페이지 수를 결정하기 때문이다.
하지만 교체 공간이 스왑 가능한 유일한 디스크 위치는 아니다. 실행 중인 프로그램의 코드 페이지는 파일로서 디스크에 존재하고 있고, 온디스크 바이너리로서 파일을 다시 스왑하면 되기 때문에 굳이 교체 공간에 보관하지 않는다.
메모리 참조 과정을 다시 떠올려보자.
가상 주소에서 VPN이 추출된다.
TLB에 항목이 있는지 확인 후, 일치하면 TLB 히트로 물리 주소를 통해 데이터에 접근한다.
TLB 미스라면 페이지 테이블 베이스 레지스터를 통해 페이지 테이블을 찾고, VPN을 통해 PTE를 조회한다.
페이지가 유효하고 실제 메모리에 있으면 PTE의 PFN을 통해 데이터를 TLB에 삽입하고 재시도한다.
여기에 디스크 스왑 과정을 넣으려면 더 많은 것이 필요하다. 특히나 하드웨어가 PTE를 확인할 때 페이지가 실제 메모리에 없는 것을 알게 되는데, 그것은 실제 비트(present bit)를 통해 판별된다.
즉 실제 비트가 1이면 메모리에 존재한다는 것이고, 0이면 디스크 어딘가에 존재한다는 의미이다.
물리 메모리에 없는 페이지에 액세스하는 행위를 일반적으로 page fault라고 한다.
page fault가 발생하면 OS가 처리하는데, page-fault handler라 불리는 코드가 실행된다.
하드웨어 관리형 TLB든, 소프트웨어 관리형 TLB든 page fault는 OS가 처리한다.
page fault를 처리하기 위해 OS가 페이지를 디스크에서 메모리로 스왑해야 되는데, 디스크 내의 위치를 알아내기 위해 페이지 테이블을 사용한다. 페이지 테이블의 PTE의 비트를 통해 주소를 찾는다.
디스크 입출력이 완료되면 OS는 페이지 테이블을 업데이트하고, PTE의 PFN 정보를 업데이트한다. 이후 명령은 재시도 된다.
재시도에서는 TLB 미스가 발생하고, TLB를 업데이트하면 다시 재시도하여 TLB 히트를 달성한다.
디스크 입출력이 작동하는 동안에는 프로세스가 차단된 상태가 되는데, page fault를 처리하는 동안 OS는 준비 상태의 다른 프로세스를 자유롭게 실행할 수 있다. 멀티프로그래밍 시스템이 하드웨어를 효과적으로 사용하는 방식이다.
💡 page fault를 OS가 처리하는 이유
OS를 신뢰하지 않는 하드웨어 관리형 TLB에서도 왜 OS를 통해 처리할까? 첫째로 디스크에 대한 page fault는 느리기 때문에, OS가 처리하더라도 오버헤드가 최소화되기 때문이다. 둘째로, page fault를 처리하려면 교체 공간 등의 기타 세부 사항을 이해할 필요가 있는데, 그것을 위해 하드웨어 대신 OS가 처리를 담당하게 된다.
OS는 새로운 페이지를 위해 공간을 확보해야 하고, 하나 이상의 페이지를 먼저 page out할 수 있다. 이때, 제거 혹은 교체할 페이지를 선택하는 프로세스를 페이지 교체 정책(page-replacement policy)이라고 한다.
잘못된 교체 결정을 내리면 메모리가 디스크처럼 매우 느린 속도로 실행될 수 있어서 올바른 교체 정책을 적립하기 위한 많은 고민이 있어 왔다. 추후에 더 알아보자.

페이지가 존재하고 유효한 경우, PTE에서 PFN을 가져온 뒤 TLB를 갱신하고 재시도하여 TLB 히트로 이어진다.
메모리에 존재하지 않는 경우 page-fault handler가 실행된다.
또 다른 경우는 접근 불가한(protect bit를 어긴) 경우인데 이런 경우는 OS trap handler가 실행되어 결과적으로 프로세스가 종료될 것이다.

새 페이지를 위해 빈 공간을 찾음
없으면 기존 페이지를 page out함
디스크 입출력 요청을 시작함
작업이 완료되면 페이지 테이블 업데이트 및 재시도
실제로 OS에서는 페이지를 제거하기 시작할 시기를 따로 정해두는데, 이것에 high watermark(HW)와 low watermark(LW)를 사용한다.
사용 가능한 페이지 수가 LW보다 작을 시, 메모리 확보를 담당하는 백그라운드 스레드가 생성되고, HW만큼 확보될 때까지 page out을 수행한다.
이 백그라운드 스레드는 스왑 데몬 혹은 페이지 데몬이라고 불리며, 메모리를 확보한 후에 절전 모드로 전환된다.
한번에 여러 페이지를 처리하는 것으로 또 다른 최적화도 가능한데, 많은 시스템에서는 페이지를 클러스터화(그룹화)해서 한번에 교체 공간에 기록한다. 그것으로 디스크 탐색 및 회전 오버헤드를 줄이면서 성능을 향상시킨다.
물리 메모리보다 더 많은 메모리에 접근하는 개념을 swapping으로 달성할 수 있다. 이것을 위해 present bit가 필요하고, 페이지 테이블 구조가 더 복잡해진다.
메모리에 페이지가 없다면 page-fault handler가 동작하여 디스크에서 메모리로 페이지를 가져오는데, 이때 공간을 확보하기 위해 일부 페이지를 page out 하기도 한다.
경우에 따라 여러 번의 디스크 입출력이 필요할 수도 있고, 간단한 작업에도 수 밀리초가 걸리는 최악의 상황도 발생한다. 그래서 교체 정책이 굉장히 중요하다.