가상 메모리
훑어보는 메모리 관리에서 언급한 바와 같이 사용자들이 시스템에 탑재된 물리 메모리보다 훨씬 크게 느껴지는 메모리를 말한다. 예를 들어, 현재 진행 중인 모든 프로그램의 크기의 총합이 10mb라면 이 시스템의 메모리 크기는 10mb 이상이라는 환상을 주는데 가상 메모리 덕분이다.
가상 메모리는 반드시 프로그램 전체가 적재되어야 한다는 전제하에서는 결코 얻어질 수 없다. 프로그램 전체가 적재가 아니라면 프로그램의 분리가 필연적으로 요구되고, 분리된 조각 중 필요한 부분만 적재되어야 한다.
가상 메모리 환경에서는 분리된 프로그램 일부분만 메모리에 적재되고 나머지는 디스크에 존재한다. 이때, 필요에 따라서 메모리에 적재된 내용을 디스크로 보내거나 디스크 내용을 메모리로 적재하는 스와핑(Swapping)이 빈번하게 일어난다.
- 가상 메모리는 분리된 프로그램 조각 중, 필요한 일부 몇 개만 적재함으로써 얻어질 수 있다.
- 가상 메모리 관리 기법은 페이징을 기반으로 한다.
1. 요구 페이징 (Demand Paging)
가상 메모리를 제공하기 위해 확장한 페이징을 요구 페이징이라 하는데, 명칭과 같이 실시간으로 요구가 있을 때 해당 페이지를 적재한다는 뜻이다.
요구 페이징은 페이지 테이블에 대한 약간의 수정과 하드웨어 인터럽트를 활용해서 구현할 수 있다. 페이지 테이블의 각 항목에 유효 비트(Valid/Invalid)를 추가하고, 지금 참고하고자 하는 페이지가 부재이면(유효하지 않으면) 인터럽트가 발생하여 대응되는 인터럽트 핸들러에 의해서 해당 페이지를 적재하도록 하면 이것이 요구 페이징이 된다.
페이지를 디스크에서 읽어 들이는 과정을 스왑 인(Swap In), 적재된 페이지를 디스크로 저장하는 과정을 스왑 아웃(Swap out)이라 하고, 이들 두 과정을 스와핑(Swapping)이라 한다. 스왑 아웃은 메모리가 부족할 때 이루어지고, 이때 스왑 아웃시킬 페이지를 선택하는 일을 페이지 교체라고 한다.
2. 페이지 부재 트랩(Page Fault Trap) 처리
요구 페이징의 가장 큰 장애 요인 중 하나는 페이지 부재로 인한 프로세스 처리 지연이다. 부재 페이지 때문에 메모리가 스왑 인 되는 과정은 엄연한 입/출력이므로 해당 프로세스는 입/출력이 완료될 떄까지 대기 상태로 전환되어야 하고, 프로세스 처리는 그만큼 지연될 수밖에 없다.
이러한 처리 지연을 최소화하기 위해서는 페이지 부재 트랩 처리 과정에 대한 명확한 이해가 필수적이다.
- CPU가 페이지 1에 있는 load 기계 명령어를 인출하여 피연산자 주소를 분리한다. (결과: 페이지 4(E))
- CPU가 다시 피연산자를 인출하기 위해 페이지 4(E)에 대한 접근으로 시도한다.
- MMU는 CPU가 보내온 주소(페이지 4, E)를 물리 주소로 변환하기 위해 페이지 테이블을 참조한다.
- MMU는 페이지 테이블을 참조해서 해당 페이지가 부재이므로 페이지 부재 트랩(인터럽트)이 발생하고, CPU는 해당 운영체제에 있는 페이지 부재 핸들러로 점프한다.
- 운영체제는 페이지 부재 트랩 핸들러는 페이지 테이블에 기록된 디스크 정보를 참조하여 디스크의 12번 블록을 확인한다. (페이지 4(E) -> 디스크 12번 블록)
- 물리 메모리에서 비어있는 2번 페이지 프레임을 발견하여 입/출력 채널에 디스크의 12번 블록을 읽어 들이도록 명령을 설정한 후, CPU를 다른 프로세스 할당한다. (문맥 교환(context switch))
- 입/출력 채널로부터 입력 완료 인터럽트를 받으면 다시 페이지 부재 트랩 핸들러로 진입하여, 페이지 테이블의 4번 항목의 페이지 프레임 번호를 2로 변경하고,
- 유효 비트를 무효(i)에서 유효(v)로 변경한 후,
- 페이지 부재 트랩 핸들러를 마치면서, 프로세스의 페이지 부재를 일으켰던 기계 명령어부터 재실행하도록 한다.
실행 중인 프로세스들의 요구 페이징이 진행되고 적재 프로세스의 수가 늘어나면 결국에는 메모리 전체가 사용 상태에 이르게 된다. 이처럼 메모리에 비어있는 페이지 프레임이 없는 상황에서, 요구 페이징이나 새로운 프로세스의 적재를 위한 페이지 프레임이 필요할 때는 사용 중인 페이지 프레임 하나를 희생양(Victim)으로 선택해서 그 내용을 디스크에 보관 시켜 빈 페이지 프레임을 확보해야 하는데 이를 페이지 교체라고 한다.
페이지 교체 작업 시에는 페이지 부재 처리 시간은 페이지 보관 시간이 추가되어 2배로 늘어나고, 교체될 페이지로 어떤 것을 선택하느냐의 문제는 시스템의 성능을 좌우하는 중요한 상황이다. (만약 바로 다음에 참조될 페이지가 교체된다면 최악의 선택이 되고, 프로세스 처리속도가 늦어진다.)
- 최적(OPT: Optimal) 페이지 교체
사용되지 않거나 가장 오랫동안 사용되지 않을 페이지를 교체하는 것이다. 미래에 가장 오랫동안 참조되지 않을 페이지를 교체하는 이상적인 기법이지만, 프로세스의 미래 진행을 예측할 수 없으므로 구현할 수 없다.
- FIFO(First-In First-Out) 페이지 교체
가정 먼저 적재된 페이지 즉, 가장 오래된 페이지를 교체한다. 그러나 만약 그것이 현재 참조가 왕성하게 일어나고 있는 페이지라면 최악의 선택이 된다.
- LRU(Least Recently Used) 페이지 교체
적재된 모든 페이지를 참조 순서에 따라 스택으로 관리하여 가장 오랫동안 사용되지 않은 페이지를 교체한다.
모든 페이지를 일렬로 순서화시켜야 하므로 최대 할당 페이지 프레임 수가 커지면 스택 관리 부담이 크다.
- NUR(Not Used Recently) 페이지 교체
비교적 최근에 사용되지 않은 페이지를 교체하는 방법으로, 모든 페이지 사이의 참조 순서를 일렬로 가르지 않고 참조된 등급과 참조되지 않은 등급으로 분류하는 방법을 사용해서 관리 부담을 줄였다.
- 운영체제는 주기적으로 모든 페이지의 참조 비트를 0으로 청소한다.
- 하드웨어 참조가 있을 때마다 해당 페이지의 참조 비트를 1로 표시한다.
- 페이지 교체가 필요하면 먼저 참조 비트가 0인 페이지 중에서 희생 페이지를 선택한다.
참조 비트 외에 부가 비트를 사용하면 참조 그룹을 보다 세분화시킬 수 있다. 예를 들어, 여덟 개의 비트 중 최상위 비트를 참조 비트로 사용하고, 주기적으로 전체 비트를 오른쪽으로 한 비트씩 이동시키면 최근 8주기 동안 일어난 각 페이지에 대한 참조 패턴을 256(2^8)가지 등급으로 관리할 수 있다.
참조 비트는 하드웨어에 의해 쉽게 구현될 수 있으므로 현대 운영체제는 대부분 이 기법을 사용한다.
4. 페이지 버퍼링(Page Buffering)
몇 개의 가용 페이지 프레임을 미리 풀로 관리하고 있다가, 페이지 교체가 필요할 때 우선 풀에서 빈 페이지 프레임을 즉시 할당하고, 사후에 새로운 교체 페이지를 선택하여 디스크 기록 후 풀에 등록하는 것을 페이지 버퍼링이라 한다.
페이지 버퍼링은 풀에 등록된 몇 개의 페이지 프레임들에 대한 활용도는 낮아지지만 교체될 페이지의 디스크 저장과 새로운 페이지의 적재 사이에서 시간적 완충을 제공함으로써 페이지 교체에 따른 프로세스 지연 시간을 줄인다.