在 Linux 性能优化和内核相关的面试中,page cache、buffer cache 以及脏页回写机制是高频考点。很多候选人能说出“缓存磁盘数据”这样的模糊概念,但一旦追问两者区别、脏页何时写回、如何观测和调优,就容易卡壳。这篇文章从面试题的角度出发,把这三个概念串成一条完整的知识链,帮助你从容应对。
一、先分清 page cache 和 buffer cache
核心结论:在现代 Linux 内核中,page cache 和 buffer cache 已经统一,但历史上有明确分工。
- page cache:以内存页(通常 4KB)为单位,缓存文件内容。当你
read()一个普通文件时,内核会把文件数据读入 page cache,下次访问直接命中内存。它面向的是“文件”这一层抽象。 - buffer cache:早期内核中以块设备块为单位,缓存磁盘块的原始数据,主要服务于文件系统元数据(如超级块、inode 位图)以及直接操作块设备的场景。
在 Linux 2.4 之前,两者是独立的两套缓存,同一份磁盘数据可能同时存在于 page cache 和 buffer cache 中,造成内存浪费和一致性问题。从 2.4 开始,内核将 buffer cache 合并进 page cache:块设备的数据也通过 page cache 管理,buffer_head 变成描述 page 内某个块的辅助结构。
所以面试时如果被问“page cache 和 buffer cache 有什么区别”,可以这样回答:
在旧内核中,page cache 缓存文件页,buffer cache 缓存块设备块,两者独立。现代内核已将 buffer cache 合并到 page cache,统一用 address_space 管理,buffer_head 只是 page 内块映射的辅助结构。因此今天说“文件缓存”基本等同于 page cache。
二、脏页是怎么产生的
当应用程序或内核修改了 page cache 中的页,这个页就与磁盘上的数据不一致,被称为脏页(dirty page)。
典型场景:
- 调用
write()写入文件,数据先写入 page cache,对应页被标记为脏; - 通过
mmap()映射文件并修改内存,也会产生脏页; - 文件系统元数据更新同样会产生脏页。
脏页不会立即写回磁盘,而是由内核在合适的时机批量回写,这样能合并大量小写入、减少磁盘 I/O 次数,显著提升性能。但代价是:如果系统在脏页写回前掉电,数据就会丢失。
三、脏页回写的触发机制
这是面试中最容易展开的部分。脏页回写主要由以下几个机制触发:
1. 周期性回写(writeback 内核线程)
内核为每个块设备创建 flush 内核线程(如 flush-8:0),周期性地扫描并回写超过一定“年龄”的脏页。相关参数在 /proc/sys/vm/ 下:
dirty_expire_centisecs:脏页过期时间,默认 3000(即 30 秒)。超过这个时间的脏页会被回写线程写回。dirty_writeback_centisecs:回写线程唤醒间隔,默认 500(即 5 秒)。
2. 阈值触发回写
当脏页占系统内存的比例超过阈值时,内核会主动回写:
dirty_background_ratio/dirty_background_bytes:后台回写阈值,默认 10%。达到后,回写线程在后台异步写回,不阻塞应用。dirty_ratio/dirty_bytes:强制回写阈值,默认 20%。达到后,写入进程会被阻塞,同步等待脏页写回,直到比例降下来。这是很多“系统突然卡顿”的元凶。
3. 显式同步
sync():把所有脏页刷到磁盘,返回后数据仍在磁盘缓存中;fsync():把指定文件的脏页和元数据刷到磁盘,并等待磁盘确认;fdatasync():类似 fsync,但只刷数据,不强制刷不影响读取的元数据。
面试常问:write() 返回成功是否代表数据已落盘?答案是否,只代表写入了 page cache。要保证持久化,必须调用 fsync() 或 fdatasync()。
四、脏页回写与内存回收的关系
脏页在回写完成前不能被直接回收。当内存紧张需要回收页面时:
- 干净页:直接丢弃,下次需要再从磁盘读;
- 脏页:必须先写回磁盘变成干净页,才能回收。
如果回写速度跟不上脏页产生速度,内存回收就会受阻,进而触发直接回收(direct reclaim),导致分配内存的进程被阻塞,表现为系统响应变慢甚至“假死”。
五、如何观测和调优
面试官常接着问:“线上发现 I/O 抖动或写入延迟高,你怎么排查?”
观测工具:
cat /proc/meminfo | grep -i dirty:查看 Dirty、Writeback 等内存量;vmstat 1:观察bo(块设备写出)和wa(I/O 等待)列;iostat -x 1:查看磁盘%util、await、aqu-sz;iotop:定位是哪个进程在大量写;bpftrace/perf:追踪writeback相关内核函数。
调优思路:
- 写入突发大时,适当降低
dirty_ratio和dirty_background_ratio,避免一次性积累过多脏页导致强制回写阻塞; - 对延迟敏感的服务,应用层主动、及时
fsync(),把大块回写拆成小块; - 数据库等场景可考虑
O_DIRECT绕过 page cache,自行管理缓存; - 使用 cgroup v2 的
io控制器限制写入速率,防止单个进程打满磁盘。
六、面试常见追问
-
“page cache 会占用所有内存吗?”
不会。它是可回收的,内存紧张时干净页会被丢弃。但脏页需要先回写,所以大量脏页会挤占可用内存。 -
“为什么我的程序 write 很快,但 fsync 很慢?”
write 只是写内存,fsync 要等磁盘真正落盘,受磁盘带宽、队列深度、文件系统日志等影响。 -
“dirty_ratio 和 dirty_background_ratio 的区别?”
前者是同步阻塞回写的硬阈值,后者是异步后台回写的软阈值。 -
“mmap 写文件后不 msync 会怎样?”
数据仍在 page cache,由内核按脏页机制回写;进程退出不会自动保证落盘,掉电可能丢失。
总结
掌握这条主线即可应对大多数面试:page cache 缓存文件页,buffer cache 已合并其中;写操作产生脏页;脏页由周期回写、阈值回写和显式 sync 触发写回;回写不及时会阻塞内存回收和写入进程;通过 /proc/sys/vm 参数和观测工具进行调优。 把这几个环节讲清楚,再结合一两个线上排查案例,就能体现出你对 Linux 内存与 I/O 子系统的真实理解。
未经允许不得转载:任鹏个人博客 » Linux 面试题:page cache、buffer cache 与脏页回写机制

