K05 — Page Cache: A Unified Cache for File Data and Anonymous Memory
Key Insight: Linux does not waste any memory. It turns all free memory into cache.
The Concept of Page Cache
Page Cache is Linux’s mechanism for caching file data and anonymous memory. It turns physical memory into a cache for disk blocks, accelerating file I/O and managing process anonymous memory.
Page Cache 的核心思想:
1. 所有磁盘 I/O 都经过 Page Cache
- 读文件:先查 Page Cache,命中则直接返回
- 写文件:先写入 Page Cache,定期写回磁盘
2. Page Cache 和 buddy system 的关系
- Page Cache 的页面来自 buddy system
- 释放 Page Cache 时,页面归还 buddy system
3. 两种页面类型:
- file-backed page:映射到文件的页面
- anonymous page:进程的堆/栈,没有文件对应
两者都缓存在 Page Cache 中,都受 LRU 管理。Page Cache Flow for File Reads and Writes
读取文件时(read syscall):
1. 磁盘文件 → Page Cache → 用户 Buffer
read(fd, buf, size):
→ VFS 接收请求
→ 查找 Page Cache(radix tree)
Cache Hit:
→ 直接从 Page Cache 读取(无磁盘 I/O)
→ 耗时:~100ns(内存访问)
Cache Miss:
→ 从 buddy system 分配页面
→ 发起磁盘 I/O(读写磁盘)
→ 数据写入 Page Cache
→ 返回给用户
2. 写文件时(write syscall):
write(fd, buf, size):
→ 直接写入 Page Cache(不立即写磁盘)
→ 设置页面为 dirty
→ 标记 inode 为 dirty
→ 立即返回(快速写入)
→ 后台 pdflush/flush 线程写回磁盘(延迟写回)
好处:写入比实际磁盘 I/O 快 100~1000 倍Radix Tree: Page Cache Index Structure
Page Cache 用 radix tree 快速查找:
struct address_space {
struct radix_tree_root page_tree; // radix tree 根
spinlock_t tree_lock; // 保护锁
unsigned long nrpages; // 页面总数
// ...
};
查找 key = (inode, offset):
→ 线性扫描太慢 → 用 radix tree
→ O(log N) 查找,N = 页面数量
→ 每个文件一个 radix tree
radix tree 的特点:
- 稀疏数组(sparse array)
- 只有存在的页面才占用节点
- 节省内存(不需要预分配整个数组)
- 查找:给定文件偏移,快速找到对应页面
radix_tree_insert():
→ 把 page 对象插入 tree
→ key = offset >> PAGE_SHIFT
radix_tree_lookup():
→ 给定 offset,查找 page
→ 返回 struct page *Anonymous Pages and Page Cache LRU
匿名页面(anonymous pages)也走 LRU:
当物理内存紧张时,kswapd 回收页面:
- 优先回收文件页(干净 → 直接释放,脏 → 写回)
- 其次回收匿名页(需要 swapout 到磁盘)
LRU 链表结构:
struct zone {
// 文件页链表
struct list_head[NR_LRU_LISTS] fileInactive;
struct list_head[NR_LRU_LISTS] fileActive;
// 匿名页链表
struct list_head[NR_LRU_LISTS] anonInactive;
struct list_head[NR_LRU_LISTS] anonActive;
};
LRU_LIST 分类:
- LRU_INACTIVE_ANON:匿名冷页(最近没访问)
- LRU_ACTIVE_ANON:匿名热页(最近访问过)
- LRU_INACTIVE_FILE:文件冷页
- LRU_ACTIVE_FILE:文件热页
回收优先级(默认):
1. file cold(最优先,因为直接释放)
2. anon cold(需要 swapout)
3. file hot(脏文件先写回)
4. anon hot(最不愿意回收)Page Cache Read-Ahead
Page Cache 的预读(readahead):
预读的原理:
- 应用可能顺序访问文件
- 内核预测下一步访问,提前把页面加载到 Page Cache
- 减少磁盘 I/O 等待
预读触发(典型):
1. 应用顺序读文件(read(fd, buf, size))
2. Page Cache miss → 分配页面 → 从磁盘读
3. 检测到顺序访问模式
4. 预读 2~4 个额外页面(提前到 Page Cache)
查看预读效果:
$ strace -e trace=read ./program 2>&1 | head -20
read(3, "hello world...", 4096) = 4096
→ 每次 read 4KB
$ cat /proc/self/maps
7f... rw-p ... // 内存映射文件
如果是 mmap,顺序访问会触发 readahead:
→ 内核自动把后续页面读入 Page CacheSummary
- Page Cache: Cache for all disk I/O; all file reads and writes go through it
- Cache Hit/Miss: Hit returns directly (~100ns); Miss allocates page → disk I/O
- radix tree: One radix tree per file; key = offset; O(log N) lookup
- Anonymous Pages: Anonymous pages (heap/stack) also use LRU; reclamation requires swapout
- LRU Classification: file cold > anon cold > file hot > anon hot (reclaim priority)
- Readahead: On sequential access, kernel pre-loads subsequent pages into Page Cache
Next (K06): When a user program calls mmap, how does the kernel allocate physical pages step by step? Demand paging, page fault handling, and anonymous mapping allocation flow.
Comments