운영체제 아주 쉬운 세가지 이야기 8주차(물리 메로리 크키의 극복: 메커니즘)

2025. 10. 29. 19:03개념 공부

 물리 메모리를 넘어서 — Swap, Page Fault, 그리고 진짜 가상 메모리의 작동 원리

 시작 — 왜 ‘물리 메모리를 넘어’야 할까?

지금까지는 모든 프로세스가 “물리 메모리 안에 전부 들어있다”고 가정했죠. 하지만 현실은 훨씬 가혹합니다. 실행 중인 모든 프로그램의 주소 공간을 동시에 메모리에 담을 수는 없어요.

그래서 운영체제는 “당장 쓰지 않는 페이지는 어딘가에 잠시 보관”하기로 합니다. 이 임시 저장소가 바로 디스크(swap space)예요. 디스크는 느리지만, 저장 용량이 훨씬 커서 물리 메모리를 보완할 수 있습니다.

💬 즉, 스왑은 “지금 필요하지 않은 기억을 하드디스크에 잠시 맡기는” 메모리의 외장 저장장치입니다.

Swap Space — 가상 메모리의 비밀 창고

스왑 공간은 디스크 일부를 운영체제가 예약해 둔 곳입니다. 메모리에서 밀려난 페이지를 “스왑 아웃(swap out)”할 때 이곳에 저장하고, 나중에 다시 필요하면 “스왑 인(swap in)”으로 메모리로 되돌립니다.

작동 예시

예를 들어 물리 메모리엔 4페이지밖에 없고, 각 프로세스가 8페이지짜리 주소공간을 갖는다면, 나머지 절반의 페이지들은 전부 스왑 공간에 보관되어 있겠죠.

코드 페이지(프로그램의 실행 코드)는 사실상 이미 파일 시스템에 존재하므로, 스왑 대신 바이너리 파일로부터 다시 불러올 수도 있습니다. 따라서 “어디서 가져올지”를 구분해서 관리해야 합니다.

 Present Bit — 페이지가 지금 ‘메모리에 있는가?’

스왑 기능을 쓰기 위해, 페이지 테이블에 새로운 플래그가 추가됩니다: Present Bit.

  • Present = 1 → 페이지가 물리 메모리에 존재함
  • Present = 0 → 페이지가 디스크(스왑 영역)에 있음

CPU가 메모리를 접근하려고 할 때, 하드웨어는 TLB → 페이지 테이블 순으로 변환을 시도합니다. 그런데 해당 PTE의 Present Bit이 0이라면? 바로 Page Fault가 발생합니다.

💬 페이지 폴트(Page Fault)는 “잘못된 접근”이 아니라 “아직 안 불러온 페이지 접근”일 수도 있습니다.

 Page Fault — OS가 개입하는 순간

페이지 폴트가 발생하면 CPU는 “예외(exception)”을 발생시키고, 제어권을 OS의 Page Fault Handler로 넘깁니다.

핸들러는 아래 순서로 일을 처리합니다:

  1. 요청한 페이지의 위치(디스크 주소)를 PTE에서 확인
  2. 디스크 I/O 요청으로 해당 페이지를 읽어 메모리로 복사
  3. 페이지 테이블의 Present Bit을 1로 바꾸고 PFN 갱신
  4. 명령어를 다시 시도(retry) → 이번엔 TLB Miss → 다시 시도 → 성공!

이때 디스크 I/O가 매우 느리기 때문에, 프로세스는 blocked 상태로 전환되고, OS는 그 사이에 다른 프로세스를 실행시켜 CPU 낭비를 줄입니다.

 “왜 하드웨어가 직접 처리하지 않을까?”

TLB는 하드웨어가 직접 관리하지만, Page Fault는 OS가 처리합니다. 이유는 단순합니다:

  • 디스크 I/O는 수천~수만 배 느리므로, OS가 천천히 처리해도 큰 차이 없음
  • 하드웨어는 디스크 구조나 스왑 주소 등을 모름 — OS가 더 유연하게 처리 가능

즉, “빠른 일(TLB Miss)”은 하드웨어가, “느리고 복잡한 일(Page Fault)”은 OS가 맡는 셈입니다.

 Page Fault Control Flow 요약

if (TLB Hit)
    Access memory
else
    Read PTE from memory
    if (Present == 1)
        TLB에 추가하고 재시도
    else
        PAGE_FAULT 발생 → OS 핸들러 실행

OS가 실행되면:

PFN = FindFreePage()
if (PFN == -1)
    PFN = EvictPage()   // 교체 정책 실행
DiskRead(PTE.DiskAddr, PFN)
PTE.Present = 1
PTE.PFN = PFN
RetryInstruction()

이 일련의 과정이 완전히 투명하게 수행된다는 점이 핵심이에요. 프로세스는 자신이 디스크에서 페이지를 가져오고 있다는 사실조차 모릅니다.

메모리가 꽉 찼을 때 — Page Replacement

만약 메모리가 가득 차서 새로운 페이지를 불러올 공간이 없다면, OS는 교체 정책(Page Replacement Policy)을 사용합니다.

어떤 페이지를 내보낼지 결정하는 게 핵심입니다. 잘못된 페이지를 내보내면, 곧바로 다시 불러오게 되어 성능이 폭락합니다.

💬 잘못된 교체는 디스크 속도(수천 배 느림)로 프로그램이 실행되는 비극을 초래합니다.

 실제 교체는 언제 일어날까?

단순히 “메모리가 꽉 찼을 때만 교체”하는 건 비효율적이에요. 대부분의 OS는 항상 일정량의 여유 메모리를 유지합니다.

  • Low Watermark (LW): 여유 메모리가 이보다 작아지면 교체 시작
  • High Watermark (HW): 이 정도 여유가 확보될 때까지 페이지를 내보냄

교체 작업은 백그라운드에서 “페이지 데몬(Page Daemon)”이 수행합니다. 이 데몬은 한꺼번에 여러 페이지를 모아 디스크에 기록(클러스터링)하여 성능을 높이죠.

💬 ‘데몬(daemon)’은 사실 “백그라운드에서 묵묵히 일하는 프로세스”라는 뜻이에요.

전체 그림 요약

  • 스왑 공간: 물리 메모리를 넘어서는 저장소
  • Present Bit: 페이지의 “위치”를 알려주는 플래그
  • Page Fault: 스왑된 페이지를 다시 불러오는 트리거
  • Page Replacement: 가득 찬 메모리에서 공간 확보
  • Page Daemon: 백그라운드에서 메모리 청소하는 관리자

프로세스 입장에서는 여전히 “자기 메모리 전체가 존재”하는 것처럼 보이지만, 현실에서는 일부는 메모리에, 나머지는 디스크에 흩어져 있습니다.

이것이 바로 현대 OS의 가상 메모리 시스템이 제공하는 “거대한 환상”이죠.