Linux 面试题:page cache、buffer cache 与脏页回写机制

在 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:查看磁盘 %utilawaitaqu-sz
  • iotop:定位是哪个进程在大量写;
  • bpftrace / perf:追踪 writeback 相关内核函数。

调优思路:

  • 写入突发大时,适当降低 dirty_ratiodirty_background_ratio,避免一次性积累过多脏页导致强制回写阻塞;
  • 对延迟敏感的服务,应用层主动、及时 fsync(),把大块回写拆成小块;
  • 数据库等场景可考虑 O_DIRECT 绕过 page cache,自行管理缓存;
  • 使用 cgroup v2 的 io 控制器限制写入速率,防止单个进程打满磁盘。

六、面试常见追问

  1. “page cache 会占用所有内存吗?”
    不会。它是可回收的,内存紧张时干净页会被丢弃。但脏页需要先回写,所以大量脏页会挤占可用内存。

  2. “为什么我的程序 write 很快,但 fsync 很慢?”
    write 只是写内存,fsync 要等磁盘真正落盘,受磁盘带宽、队列深度、文件系统日志等影响。

  3. “dirty_ratio 和 dirty_background_ratio 的区别?”
    前者是同步阻塞回写的硬阈值,后者是异步后台回写的软阈值。

  4. “mmap 写文件后不 msync 会怎样?”
    数据仍在 page cache,由内核按脏页机制回写;进程退出不会自动保证落盘,掉电可能丢失。

总结

掌握这条主线即可应对大多数面试:page cache 缓存文件页,buffer cache 已合并其中;写操作产生脏页;脏页由周期回写、阈值回写和显式 sync 触发写回;回写不及时会阻塞内存回收和写入进程;通过 /proc/sys/vm 参数和观测工具进行调优。 把这几个环节讲清楚,再结合一两个线上排查案例,就能体现出你对 Linux 内存与 I/O 子系统的真实理解。

未经允许不得转载:任鹏个人博客 » Linux 面试题:page cache、buffer cache 与脏页回写机制

赞 (0) 打赏

评论 0

取消
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏