Page Cache, Buffer Cache
OS의 페이지 캐시와 버퍼 캐시는 디스크 IO 성능을 높이기 위해 메인 메모리 RAM의 일부를 저장소로 사용하는 핵심 메커니즘이다.
최신 리눅스 커널에서는 이 두개념이 하나로 통합되었지만, 기원과 논리적 구분은 공부할 가치가 있는듯하다.
구분을 먼저 해보면 페이지 캐시는 page 단위(보통 4kb), 버퍼 캐시는 블록 단위(512B~4KB)이다.
페이지 캐시는 파일의 내용 file data가 대상이고 고수준(파일 시스템 계층)에서 추상화 되어있으며, 목적으로는 파일 읽기/쓰기 속도 향상을 위해 사용된다.
버퍼 캐시는 디스크의 물리적 블록이 대상이고. 저수준(block device) 계층에 추상화 되어있으며 디스크 메타데이터 및 블록 정렬 관리 목적이다.
- 페이지 캐시: 사용자가
read(),wrtie()시스템 콜을 통해 파일에 접근할 때 사용된다. 파일의 100번째 바이트부터 읽어줘 라는 요청을 처리한다. - 버퍼 캐시: 파일 시스템의 메타데이터(inode, Superblock등)나 디스크 장치에 직접 접근할 때 사용된다. 디스크의 500번째 섹터를 읽어줘라는 요청을 처리한다.
통합 페이지 캐시 (Unified Buffer Cache) #
과거 구조의 가장 큰 문제는 **이중 캐싱(Double Caching)**이었다. 파일 데이터를 읽으면 페이지 캐시에 저장되고, 그 아래 블록 계층에서 똑같은 데이터가 버퍼 캐시에 또 저장되어 메모리가 낭비됐기 때문이다.
현대 리눅스 커널 (linux 2.4이후) 이를 해결하기 위해 통합 페이지 캐시 구조를 채택했다.
- 구조: 버퍼 캐시는 이제 독립적인 메모리 영역이 아니라, 페이지 캐시 내부에 포함된 논리적인 개념이다.
- 작동방식: 페이지 캐시의 한 페이지 4kb가 디스크 블록 1kb 4개를 가리키는
buffer_head구조체를 가짐으로써 두 캐시의 역할들 동시에 수행한다.
struct buffer_head {
unsigned long b_state; /* 버퍼의 상태 플래그 (Dirty, Up-to-date 등) */
struct buffer_head *b_this_page;/* 같은 페이지 내의 다음 buffer_head를 가리킴 (순환 연결 리스트) */
struct page *b_page; /* 이 버퍼가 속해 있는 물리 페이지 */
sector_t b_blocknr; /* 디스크상의 논리 블록 번호 (Logical block number) */
size_t b_size; /* 블록의 크기 (예: 1024 bytes) */
char *b_data; /* 페이지 내에서 데이터가 시작되는 위치의 포인터 */
struct block_device *b_bdev; /* 이 블록이 속한 블록 장치 (하드디스크 등) */
bh_end_io_t *b_end_io; /* I/O 완료 시 호출될 콜백 함수 */
void *b_private; /* b_end_io용 개인 데이터 */
atomic_t b_count; /* 사용 횟수 (Reference count) */
};
커널 레벨에서 읽기/쓰기 흐름 #
Read 흐름 (Page Cache Look-up)
- 프로세스가 read()를 호출한다.
- 커널은 해당 파일의 해당 오프셋이 page cache에 있는지 확인한다
address_space구조체 참조 - Cache Hit: 메모리에서 즉시 데이터를 복사하여 사용자에게 전달한다. (디스크 접근 없음)
- Cache Miss: 디스크에서 데이터를 읽어 페이지 캐시에 채운 뒤 전달한다. (이때 주변 블록까지 미리 읽는 Read-ahead를 수행한다.)
Write 흐름 (Write-back 정책)
- 프로세스가
write()를 호출한다. - 데이터를 페이지 캐시에 쓰고, 해당 페이지를 Dirty Page로 표시한다.
- 특정 조건(시간 경과, 메모리 부족, 명시적 sync)이 충족되면 커널의 Background Worker(flusher thread)가 디스크에 실제 기록을 수행한다.
관측 #
free -m을 통해서 buff/cache 항목을 보면 이 두 캐시가 사용하는 메모리 양을 확인할 수 있다.
/proc/meminfo: Cached, Buffers, Dirty 항목을 통해 상세한 수치도 확인 가능하다.
vmstat 1: bi, bo 각각 block in block out이고 이를 통해 캐시와 디스크 간의 데이터 이동을 실시간 모니터링 가능하다.