← 문제로

7. 페이지 폴트, 디맨드 페이징, 스와핑

난이도 중
내 답안
모범답안

모범답안 — 페이지 폴트, 디맨드 페이징, 스와핑

난이도: 중

핵심 답변

  • 페이지 폴트: CPU가 접근한 가상 페이지가 현재 물리 메모리에 매핑돼 있지 않아 발생하는 트랩. OS의 폴트 핸들러가 개입해 매핑을 만들어 주고 명령을 재시도한다.
  • 마이너 폴트(soft): 페이지가 이미 물리 메모리 어딘가에 있거나(공유/페이지 캐시), 0으로 채울 새 페이지만 필요한 경우. 디스크 I/O 없이 매핑만 손봐서 상대적으로 싸다.
  • 메이저 폴트(hard): 필요한 내용이 디스크(스왑/실행 파일/mmap된 파일)에 있어 디스크에서 읽어와야 한다. 디스크 지연(밀리초급)이라 마이너 폴트보다 수천 배 비쌀 수 있다.
  • 디맨드 페이징: 프로그램/데이터를 미리 다 안 올리고 실제 접근하는 순간 페이지를 가져오는 방식. 시작이 빠르고 메모리를 아끼지만, 첫 접근마다 폴트 비용이 든다.
  • 스와핑: 메모리가 부족하면 잘 안 쓰는 페이지를 디스크(스왑 영역)로 내보내고(page-out) 필요할 때 다시 읽는다(page-in). 이게 과도해져 시스템이 일은 안 하고 페이지 입출력만 반복하는 상태가 스래싱.

깊이 있는 설명 (메커니즘, 왜)

  • 폴트 처리 흐름: 접근 → MMU가 페이지 테이블에서 "유효 매핑 없음" 발견 → 트랩 → 커널 폴트 핸들러가 원인 판별(요구 페이징/스왑인/CoW/접근 위반). 정당하면 프레임을 할당/적재하고 페이지 테이블을 갱신한 뒤 명령 재실행. 부당하면(잘못된 포인터) SIGSEGV.
  • lazy allocation: malloc/mmap으로 큰 영역을 잡아도 OS는 즉시 물리 프레임을 주지 않는다. 처음 쓸 때 마이너 폴트가 나며 그때 0페이지를 매핑(demand-zero). 그래서 "할당했는데 RSS는 안 늘어나는" 현상이 정상이다.
  • copy-on-write(CoW): fork 시 부모/자식이 같은 물리 페이지를 읽기 전용으로 공유한다. 한쪽이 쓰면 그때 폴트가 나서 해당 페이지만 복사해 분리한다. 덕분에 fork가 즉시 메모리를 두 배로 먹지 않는다.
  • 워킹셋과 스래싱: 워킹셋 = 최근 일정 시간 동안 실제로 접근하는 페이지 집합. 모든 프로세스의 워킹셋 합이 물리 RAM을 넘으면, 한 페이지를 올리려 다른 페이지를 내보내고 곧 그게 다시 필요해지는 악순환(page-in/out 반복) = 스래싱. CPU는 디스크 대기로 한가한데 처리량이 바닥난다.

응용/실무 연결 (게임서버에서)

시나리오 진단:

  • 증상 1(지연 폭증 + CPU 한가 + 디스크 폭증) = 메모리가 RAM 한계에 닿아 스와핑/스래싱 시작. 실시간 서버엔 치명적 — 틱이 디스크 지연에 묶인다.
  • 증상 2(오래 안 본 영역 첫 접근만 느림) = 그 페이지가 스왑/파일로 빠져 있다가 다시 만질 때 메이저 폴트(page-in). 또는 디맨드 페이징으로 아직 한 번도 안 올라온 페이지의 첫 폴트.
  • 증상 3(fork 워커가 쓰기 시작하면 메모리 증가) = copy-on-write 동작. 읽는 동안은 공유라 메모리가 안 늘다가, 쓰는 페이지부터 복사되며 RSS 증가.

대책:

  • 스왑 비활성/억제: 실시간 서버는 보통 스왑을 끄거나 강하게 억제(Linux vm.swappiness 낮춤). 디스크로 밀려 지연 스파이크가 나느니 OOM이 차라리 명시적.
  • 메모리 상한·여유 확보: 피크 워킹셋이 RAM 안에 들도록 용량 산정. 컨테이너면 메모리 limit과 모니터링.
  • 사전 터치/락(prefault & pin): 기동 시 핵심 영역을 미리 접근해 폴트를 앞당기거나, mlock/large-page로 페이지를 물리 메모리에 고정해 런타임 메이저 폴트를 제거. C#이라면 GC가 만질 핵심 객체를 워밍업.
  • 워킹셋 축소: 비활성 플레이어/먼 맵 구역은 압축·언로드해 상주 메모리를 줄이고(문제 5의 타일 스트리밍과 연계), 핫 데이터만 RAM에.
  • 모니터링: major fault rate, swap in/out, RSS, % time in iowait를 상시 관측해 임계 전 경보.

흔한 오답·함정

  • 모든 페이지 폴트를 "느린 디스크 접근"으로 뭉뚱그림. 마이너 폴트는 디스크 없이 싸고, 메이저 폴트만 디스크를 친다.
  • 페이지 폴트 = 프로그램 버그(세그폴트)로 오해. 정상적 페이지 폴트(요구 페이징/CoW)는 OS의 정상 동작이고, 잘못된 접근만 SIGSEGV.
  • malloc 직후 RSS가 안 늘면 "할당 실패"로 오인. lazy allocation 때문에 정상.
  • 스왑을 "메모리 늘려주는 공짜 점심"으로 생각. 실시간 워크로드에선 지연 폭탄.
  • TLB 미스(문제 5)와 페이지 폴트 혼동. TLB 미스는 매핑이 있는데 변환만 캐시에 없는 것, 페이지 폴트는 매핑/페이지 자체가 없어 OS가 개입.

꼬리질문 대비

  • Q. 페이지 교체 알고리즘엔 뭐가 있나요? A. 이상적 OPT(미래를 알아야 함), 근사로 LRU, 실제 구현은 clock(second-chance) 등. 목표는 곧 쓸 페이지를 남기고 안 쓸 페이지를 내보내 폴트를 최소화.
  • Q. mmap한 파일을 접근하면 폴트가 어떻게 동작하나요? A. 첫 접근 시 메이저 폴트로 파일의 해당 페이지를 페이지 캐시로 읽어 매핑. 이후 접근은 마이너 폴트/히트. 파일 I/O를 메모리 접근처럼 다루는 이점(문제 8 연계).
  • Q. OOM killer는 언제 동작하나요? A. 스왑까지 동원해도 메모리가 모자라면 커널이 프로세스를 골라 강제 종료. 실시간 서버는 자기 프로세스가 희생되지 않게 우선순위/cgroup 한계를 관리해야 한다.
  • Q. 마이너 폴트가 많은 건 문제인가요? A. 대량의 첫 접근(워밍업)에서 자연스럽지만, 정상 운영 중 지속적으로 많다면 잦은 메모리 할당/해제나 나쁜 지역성의 신호일 수 있어 점검 대상.