Page Cache & I-O Stack
디스크의 느린 접근 속도를 보완하기 위해 메모리를 버퍼로 사용하는 페이지 캐시 시스템과, 사용자 요청이 장치까지 도달하는 OS 내부 입출력 경로를 다루는 학습 노드입니다.
Article
M
Me
hyunyoun's Blog
operating-systems-system-mechanicsoperating-systemssystem-mechanicsmemory-managementvirtualization-physicspage-cachei-o-stackos-storage9 min read
1. Overview
페이지 캐시와 I/O 스택(Page Cache & I/O Stack)은 CPU(나노초)와 하드디스크(밀리초) 사이의 큰 속도 격차를 줄이기 위해, 커널이 사용 가능한 RAM을 파일 데이터 캐시로 활용하는 I/O 버퍼링 구조입니다.
학습자는 앱이 파일에 "Hello"를 썼을 때 데이터가 곧바로 디스크에 내려가지 않고 커널 RAM(Page Cache)의 더티 페이지(Dirty Page)로 남았다가 나중에 기록되는 **지연 쓰기(Delayed Write)**를 이해합니다. 이어 디스크 접근 요청을 묶고 정렬해 탐색 비용을 줄이는 **I/O 스케줄러(Elevator Algorithm)**와, 데이터베이스처럼 자체 캐시를 관리하는 시스템에서 쓰는 **Direct I/O(O_DIRECT)**의 역할을 살핍니다.
2. Scope & Boundaries
In-Scope
- 페이지 캐시 아키텍처 (Page Cache): 커널 버퍼 캐시, Read-ahead(미리 읽기), 더티 페이지(Dirty Page)와 Flusher 스레드(pdflush).
- 메모리 매핑 I/O (mmap): 시스템 콜(read/write) 오버헤드 우회, 가상 메모리와 파일의 포인터 다이렉트 융합.
- I/O 제어 블록 (Block I/O Layer): 바이오(struct bio), 요청 큐(Request Queue), 블록 디바이스 계층.
- I/O 스케줄링 (I/O Schedulers): NOOP, CFQ(Completely Fair Queuing), Deadline, BFQ(Budget Fair Queuing).
Out-of-Scope
- 파일 시스템 데이터 구조 자체: 하드디스크에 폴더 트리와 아이노드(inode)를 어떻게 배치하는지에 대한 레이아웃 03-03-03. VFS & Filesystem Internals 영역.
- SSD/HDD 물리 기계학: 낸드 플래시 전하 트랩핑이나 하드디스크 자기장 기록 물리 법칙 03-04-01. Disk & SSD Physics 영역.
Boundaries
- Virtual Memory vs. Page Cache (03-03-01): 가상 메모리 페이징(03-03-01)은 프로세스 실행에 필요한 힙/스택 같은 익명 메모리(Anonymous)를 어떻게 관리하고 스왑할지 다룹니다. 페이지 캐시(03-03-02)는 파일 시스템에서 읽어온 파일 백드 메모리(File-backed)를 RAM에 임시 보관해 반복 디스크 접근을 줄이는 구조입니다. 둘은 모두 물리 RAM을 사용하므로 메모리 압박 상황에서 서로 경쟁합니다.
3. Counterexample
- 동기화 없는 지연 쓰기와 정전 (The
fsyncTragedy): 워드 프로세서에서 파일 저장 시write()시스템 콜만 호출하고 끝내면, 데이터는 디스크가 아니라 커널 RAM(Page Cache)에 더티 상태로 남을 수 있습니다. 이 상태에서 정전이 발생하면 커널이 디스크로 Flush하기 전의 변경분은 유실될 수 있습니다. 데이터베이스나 금융 기록처럼 내구성이 중요한 데이터는fsync()같은 동기화가 필요합니다. - DB의 이중 캐싱 병목 (Double Caching Trap): Oracle이나 MySQL처럼 자체 버퍼 풀을 가진 데이터베이스가 기본 Buffered I/O를 사용하면, 파일 데이터가 리눅스 커널 페이지 캐시와 DB 버퍼 풀에 중복 적재될 수 있습니다(Double Caching). DB처럼 메모리 캐시를 직접 관리하는 시스템은 상황에 따라
O_DIRECT로 페이지 캐시를 우회해 메모리 낭비와 락(Lock) 병목을 줄입니다.
4. Prerequisites
- 시스템 콜과 컨텍스트 (Basic):
read()를 때리면 커널 모드로 들어갔다가 나오는 비용이 왜 비싼지 알아야mmap의 위력을 실감합니다. (03-01-02 System Call Interface) - 페이징 물리 (Recommended): 페이지 폴트(Page Fault)가 났을 때 디스크에서 4KB 조각을 퍼 오는 구조를 알아야 그 조각을 캐싱하는 이유를 깨닫습니다. (03-03-01 Virtual Memory)
5. Learning Map
6. Learning Topics
Basic
Core Topic 01: 가용 RAM과 페이지 캐시 (Page Cache Basics)
- Why to Learn: 리눅스 서버에서
free -m을 실행했을 때 가용 RAM(Free)이 낮게 보이는 이유를 이해하고, Buff/Cache가 실제로는 파일 I/O 성능을 높이기 위한 커널 정책임을 구분하기 위해서입니다. - What to Learn:
- Concepts: 페이지 캐시(Page Cache), 버퍼 캐시(Buffer Cache, 최신 커널 통합),
free메모리 지표 분석. - Skills: 파일-백드(File-backed) 매핑 vs 익명(Anonymous) 매핑 구분.
- Tools: 리눅스
free -h,vmtouch(파일 캐시 적재 강제/확인 툴). - Trade-offs: 시스템에 RAM이 16GB이고 애플리케이션이 2GB만 사용하면, 커널은 남은 RAM을 최근 읽은 파일 캐시로 활용해 반복 디스크 접근을 줄입니다. 이후 다른 애플리케이션이 5GB를 요청하면 커널은 페이지 캐시를 회수해 할당하므로, 캐시 회수 비용이 일시적으로 발생할 수 있습니다.
- Concepts: 페이지 캐시(Page Cache), 버퍼 캐시(Buffer Cache, 최신 커널 통합),
- How to Learn:
- 1단계: 파이썬으로 1GB 텍스트 파일을 처음 읽을 때와, 바로 다시 읽을 때의 시간을 비교해 페이지 캐시 Hit가 성능에 미치는 영향을 확인합니다.
- 2단계: 크기 100GB 로그 파일을
grep으로 한 번만 훑는 작업(One-time scan)이 기존에 캐시된 DB 인덱스 파일을 밀어낼 수 있는 Cache Eviction/Cache Pollution 상황을 살핍니다.
- Implement: 파이썬 파일 읽기 스크립트를 작성합니다.
os.system("sync; echo 3 > /proc/sys/vm/drop_caches")명령어로 캐시를 비운 뒤 파일 읽기 속도를 측정하고, 즉시 다시 읽었을 때 캐시 히트로 속도가 어떻게 달라지는지 출력합니다.
Recommended
Core Topic 02: 더티 페이지와 Flusher (Dirty Pages & Sync)
- Why to Learn: 프로그램 오류가 없어도 전원 차단 시 아직 디스크에 내려가지 않은 변경분이 유실될 수 있으므로, Buffered I/O와 내구성 보장의 관계를 이해하기 위해서입니다.
- What to Learn:
- Concepts: 클린 페이지(Clean Page), 더티 페이지(Dirty Page), 지연 쓰기(Write-back / Delayed Write).
- Skills: 커널 Flusher 스레드(pdflush / bdi-writeback) 주기 튜닝,
fsync(),fdatasync(). - Tools:
sysctl vm.dirty_ratio,vm.dirty_background_ratio. - Trade-offs:
write()마다 실제 디스크에 기록(Write-through)하면 내구성은 높아지지만 프로그램 속도는 느려집니다. 반대로 RAM(Page Cache)에 먼저 쓰고 즉시 리턴(Write-back)하면 성능은 좋아지지만, Flusher가 디스크로 내리기 전에 전원이 끊기면 데이터가 유실될 수 있습니다.
- How to Learn:
- 1단계: 사용자가 파일 저장을 누르면 커널이 파일 캐시(RAM)에 데이터를 쓰고 해당 4KB 페이지를 더티(Dirty, 디스크 원본과 다름) 상태로 표시하는 흐름을 확인합니다.
- 2단계: 커널 백그라운드 스레드(Writeback)가 더티 페이지 비율(
dirty_ratio)이나 시간 조건에 따라 더티 페이지를 디스크로 내려 Clean 상태로 바꾸는 과정을 살핍니다.
- Implement: 파이썬 벤치마크를 작성합니다. 반복문 10,000번 동안 단순
file.write()를 수행했을 때의 시간과, 루프마다os.fsync(file.fileno())를 호출해 더티 페이지를 디스크로 내렸을 때의 시간을 비교합니다.
Practical
Core Topic 03: mmap과 Zero-Copy (mmap & Zero-Copy)
- Why to Learn: 디스크 파일이 페이지 캐시에 올라온 뒤
read()로 다시 사용자 버퍼에 복사되는 Double Copy 비용을 이해하고,mmap이 이를 어떻게 줄이는지 보기 위해서입니다. - What to Learn:
- Concepts:
mmap()(Memory Mapped I/O), 제로 카피(Zero-Copy),read()/write()의 오버헤드. - Skills: 대용량 파일 가상 주소 바인딩, 페이지 폴트 트리거 방식의 지연 매핑.
- Tools: 리눅스
strace를 통한 시스템 콜 패싱 분석. - Trade-offs:
read()는 데이터를 커널 캐시에서 사용자 버퍼로 복사하므로 추가 메모리 복사와 시스템 콜 비용이 발생하지만 사용법이 단순합니다.mmap()은 사용자 프로세스의 가상 주소를 파일 매핑과 연결해 복사 비용을 줄이지만, 아직 올라오지 않은 페이지 접근 시 Page Fault로 숨은 지연(Hidden Latency)이 발생할 수 있습니다.
- Concepts:
- How to Learn:
- 1단계: 1GB 파일에
mmap을 호출하면, 즉시 전체 파일을 읽는 대신 프로세스의 가상 주소 대역0x1000 ~ 0x40000000이 파일 영역에 매핑되도록 페이지 테이블을 준비합니다. - 2단계: 프로그래머가
char_ptr[500]을 접근하는 순간 Page Fault가 발생하고, 커널이 디스크 500번째 바이트가 포함된 4KB 블록을 RAM(Page Cache)으로 가져와 매핑을 완성하는 흐름을 확인합니다.
- 1단계: 1GB 파일에
- Implement: 파이썬 스크립트 작성. 전통적인
f.read()로 500MB 파일을 읽어오며 유저 버퍼를 할당하는 메모리 팽창(Htop 500MB 추가)과,mmap모듈을 써서m = mmap.mmap(f.fileno(), 0)로 바인딩한 뒤 정규식 슬라이싱을 쳤을 때 유저 메모리는 0바이트 증가하지만 페이지 캐시단에서 파일 스캔이 이뤄지는 제로 카피 덤프 비교.
Advanced
Core Topic 04: 블록 I/O 스케줄링과 Direct I/O (I/O Elevators & Direct I/O)
- Why to Learn: 여러 프로그램의 I/O 요청을 들어온 순서대로만 처리하면 하드디스크 헤드 이동(Seek)이 커질 수 있습니다. 요청을 정렬·병합(Merge)해 탐색 비용을 줄이는 블록 계층 스케줄링을 이해하기 위해서입니다.
- What to Learn:
- Concepts: 섹터(Sector), 실린더(Cylinder), 탐색 시간(Seek Time), 블록 I/O 스케줄러(CFQ, Deadline, NOOP/None), 바이오(struct bio).
- Skills: 다이렉트 I/O (
O_DIRECT, 페이지 캐시 우회), 엘리베이터 정렬(Elevator Sorting/Merging). - Tools: 리눅스
cat /sys/block/sda/queue/scheduler. - Trade-offs: 엘리베이터(CFQ) 스케줄러는 요청이 많을 때 디스크 실린더 순서에 가깝게 재정렬해 HDD의 헤드 이동 거리를 줄입니다. 반면 SSD는 물리 헤드가 없으므로 과도한 정렬이 CPU 비용만 늘릴 수 있어 NOOP/None 계열 스케줄러가 더 적합할 수 있습니다.
- How to Learn:
- 1단계: 1층, 10층, 2층, 9층 요청을 1 10 2 9 순서로 처리하는 대신, 1 2 9 10처럼 한 방향으로 정렬(Sort)하고 병합(Merge)해 Seek 비용을 줄이는 흐름을 확인합니다.
- 2단계: Direct I/O: 페이지 캐시와 미리 읽기(Read-ahead)를 우회하고,
O_DIRECT플래그를 사용해 사용자 RAM 버퍼와 디스크 섹터 사이의 DMA 경로를 직접 구성하는 방식을 살핍니다.
- Implement: 모의 디스크 스케줄러 알고리즘 파이썬 콘솔.
[50, 10, 80, 20, 90]트랙 I/O 요청이 순서대로 큐에 들어올 때, FCFS 방식으로 처리 시 헤드 총이동 거리(Seek Cost = 260)가 찍히는 반면, SCAN(엘리베이터) 알고리즘으로 내부 정렬 후 처리 시[10, 20, 50, 80, 90]으로 헤드 스윙을 한 방향으로 몰아 이동 거리 코스트(80)를 3배 압축하는 물리 최적화 로직 시연.
7. Terminology
8. References
Primary
- [P2] SWEBOK v4.0 - Software Construction / Runtime Efficiency (I/O) — Performance context.
- [P1] CS2023 - OS/Operating System Principles (I/O Management) — Core requirements.
Secondary
- [Linux Kernel Development] Robert Love — The Page Cache and Page Writeback.
- [Understanding the Linux Virtual Memory Manager] Mel Gorman — Buffer/Page management.
Industry
- [Kernel.org: The Linux I/O Stack Diagram] — De-facto industry standard map.
- [PostgreSQL Documentation: Reliability and the Write-Ahead Log] — Flush mechanics in practice.
9. Final Checklist
Primary
- '페이지 캐시'가 왜 CPU 캐시와 달리 '운영체제'에 의해 관리되는 소프트웨어적 물리 공간인지 설명 가능한가? (P1)
- 입출력 요청이 VFS를 거쳐 실제 장치 드라이버로 도달하기까지의 계층 전이 과정을 설명할 수 있는가? (P1)
Secondary
- '지연 쓰기(Write-back)' 방식이 시스템 성능을 높여주는 이점과 데이터 손실 위험 사이의 상충 관계를 설명할 수 있는가?
- 페이지 캐시가 가득 찼을 때 OS가 어떤 기준으로 기존 캐시를 비우고 새 데이터를 채우는지(Replacement) 도출할 수 있는가?
Industry
- 고성능 웹 서버 설계 시,
sendfile()이나mmap()이 사용자/커널 간 복사를 어떻게 줄여 TPS를 높이는지 제안할 수 있는가? (SFIA) - SSD 환경에서 블록 계층의 '입출력 스케줄러'가 HDD 시절의 알고리즘과 왜 다르게 설정되어야 하는지 기술할 수 있는가?