Linux 面试经典:buff 和 cache 到底有什么区别?用 vmstat 看懂 Page Cache 与 Buffer Cache
buff 和 cache 都会使用内存临时存放数据,它们到底有什么区别?“我的 free 内存怎么只剩一点了,系统是不是快要出问题了?”
本文将简要介绍 Linux 的内存机制,说明 buff 和 cache 各自缓存什么、为什么在现代内核中 buff 通常很小,并通过一个 vmstat 小实验,让两个指标分别“动起来”。
Buffer 与 Cache
教科书式的定义是:
- Buffer(缓冲区):暂存“正在传输中”的数据。当数据在速度不匹配的组件之间传输时,例如应用程序写入磁盘,数据不会立刻到达目的地,而是先进入 buffer,让内核可以把零散写入合并为更大的批次,再批量写入磁盘。这对顺序 I/O尤其有效。
- Cache(缓存):暂存“已经读取、之后可能再次使用”的数据。把常用数据保留在速度更快的存储层(RAM)中,下次使用时便不必再从较慢的磁盘读取。CPU 缓存、磁盘缓存和文件系统缓存都遵循同样的原则。
一句话总结:buffer 更注重协调数据传输,cache 更注重保留数据以便复用。1 不过,这只是一般概念,不能直接理解成 Linux 中“buffer 只负责写、cache 只负责读”。普通文件的缓冲读取(buffered read)、缓冲写入(buffered write)和 mmap() 都会经过 Page Cache;写入会先把缓存页标记为 dirty page,之后再由内核写回存储设备。2 3
Linux 中的实际含义:Page Cache 与 Buffer Cache
上面的定义是通用概念,但在 Linux 内存管理中,Buffers 和 Cached——也就是 /proc/meminfo 中的两个字段——有更加具体的含义。4
| 字段 | 缓存对象 | 典型场景 |
|---|---|---|
Cached(Page Cache) |
普通文件的内容,也包括 tmpfs / shmem;不包括 SwapCached |
cat、grep、普通文件的 buffered read / write |
Buffers(Buffer Cache) |
块设备地址空间中的原始磁盘块,以及部分文件系统通过 buffer head 管理的磁盘元数据块 | 对 /dev/sdX 执行 buffered I/O(缓冲 I/O)、部分文件系统的日志或元数据 I/O |
💡 内存统计中有两个容易忽略的细节
Cached不全是磁盘缓存。 tmpfs / shmem 也包括在Cached中;如果只想粗略估算基于磁盘的文件缓存,可以查看Cached - Shmem。在未配置 swap、且这些页面尚未释放时,这部分内存不能像干净的文件缓存页一样直接丢弃。5并非所有文件系统元数据都计入
Buffers。 只有部分文件系统的磁盘元数据块会使用 buffer head。VFS 的 inode 对象与 dentry 对象是独立的内存缓存,通常计入Slab/SReclaimable。6 4
这里还有一段值得了解的历史:早期的 Page Cache 和 Buffer Cache 是两套独立缓存;两者后来完成合并,并在 Linux 2.4 时期消除了文件数据写回所需的额外副本。现代内核中的 Buffer Cache 仍然存在,但 buffer head 会指向 Page Cache 中的页面。7 8 因此,现在的 Buffers 主要反映原始磁盘块,而不是普通文件内容;在多数工作负载中,它通常远小于 Cached。
如果在面试或排障时遇到这个问题,可以给出更准确的回答:
Cached主要是 Page Cache,也包括 tmpfs / shmem;Buffers反映块设备的原始磁盘块,以及部分文件系统使用的元数据块;inode / dentry 对象则大多位于可回收的 slab 内存中。从 procps-ng 3.3.10 开始,free默认使用buff/cache汇总显示,并加入available;当前版本的buff/cache实际为Buffers + Cached + SReclaimable。9 10
从 I/O 栈看 Page Cache 与 Buffer Cache 的位置
展开整个 Linux I/O 路径后,两者的分工会更加直观。11

Linux I/O 路径:Page Cache 与 Buffer Cache 的位置
图中的文件系统元数据指磁盘上的元数据块;VFS 的 inode / dentry 对象则属于前面提到的 slab 缓存。
- 文件系统路径:应用程序对普通文件执行
read()/write()→ VFS → Page Cache。读取命中缓存时,数据直接从 RAM 返回;缓存未命中或需要写回 dirty page 时,I/O 才通过文件系统进入 Block Layer 与磁盘。2 - 原始块设备路径:对
/dev/sdX之类的块设备执行普通 buffered I/O 时,不经过已挂载文件系统的文件映射逻辑,但仍会使用该块设备自己的页缓存映射(page-cache mapping);这些页面计入 Buffers。12 - 部分文件系统也使用 buffer head 管理日志或磁盘元数据块,但 VFS inode / dentry 对象使用的是另一套 slab 缓存。
- 两条路径最终都会汇入 Block Layer → Device Driver → 物理设备。数据库等自行管理缓存的应用程序可以使用
O_DIRECT绕过 Page Cache;I/O 仍然会经过文件系统与 Block Layer,而不是直接调用硬件。2
使用 vmstat 观察 buff 与 cache
vmstat 是观察系统内存、I/O 和 CPU 的基础工具。vmstat 1 5 表示每秒采样一次,共输出 5 组结果。第一行数据是系统启动以来的平均值,之后各行才是每个采样区间的数值。13
$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 123456 7890 56789 0 0 100 200 300 400 10 5 80 5 0
0 1 0 120000 7895 56800 0 0 150 250 320 410 12 4 78 6 0
2 0 0 118500 7898 56810 0 0 180 220 330 420 9 6 82 3 0
1 0 0 116800 7902 56825 0 0 120 210 310 430 11 3 81 5 0
0 0 0 115000 7905 56835 0 0 140 230 300 390 13 4 77 6 0
与本文相关的字段包括:
free:空闲内存buff:用作 buffer 的内存,对应/proc/meminfo中的Bufferscache:由 procps-ng 汇总计算的缓存值,当前为Cached + SReclaimablebi/bo:每秒从块设备读取或写入的 KiB;不受vmstat -S显示单位选项的影响13 14
上面的数值是示意数据。在实际观察中,如果 bi / bo 持续有读写量,而 free 下降、buff 或 cache 上升,通常表示内核正在把块设备数据或文件数据保留在内存中。仅凭这一现象并不能说明内存异常。15
实验:让 buff 和 cache 分别动起来
只看定义仍然比较抽象,直接实验最容易理解。先清空缓存,建立干净的基线:
# 将 dirty page 写回磁盘,并清空 page cache、dentry、inode cache
$ sync
$ echo 3 | sudo tee /proc/sys/vm/drop_caches
drop_caches=3 会丢弃干净的 Page Cache 和可回收的 slab 对象,包括 dentry / inode 缓存。它不会丢弃脏对象(dirty objects),因此先执行 sync 可以让实验结果更容易解读。该接口适合测试和调试,不建议用作日常“释放内存”的手段,因为后续重建缓存会产生额外的 I/O 与 CPU 开销。16
场景一:读取普通文件 → cache 上升
先确认测试文件位于基于磁盘的文件系统,而不是 tmpfs。在一个终端运行 vmstat -y 1(-y 会省略系统启动以来的第一行平均值),在另一个终端读取大文件:
$ dd if=/var/tmp/bigfile of=/dev/null bs=1M status=progress
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 7452100 2120 98500 0 0 0 0 120 210 1 0 99 0 0
1 0 0 6890200 2135 652800 0 0 524288 0 850 1600 2 12 70 16 0
1 0 0 6320400 2150 1214500 0 0 524288 0 860 1620 1 13 68 18 0
文件内容进入 Page Cache,因此在这组示意输出中,cache 每秒增加约 500 MiB,而 buff 几乎不变。实际增幅会受到文件大小、读取速度、已有缓存以及其他系统活动的影响。
场景二:直接读取原始块设备(raw block device)→ buff 上升
重新执行前面的 sync 与 drop_caches,然后使用 lsblk 确认设备名称,并把下面的 /dev/sdX 替换成实际设备。这个命令只读取设备并把数据丢弃到 /dev/null;不要把 if 与 of 写反,也不要加入 iflag=direct,否则会绕过需要观察的缓存:
$ sudo dd if=/dev/sdX of=/dev/null bs=1M count=1024 status=progress
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 7450300 2150 99200 0 0 0 0 115 200 1 0 99 0 0
1 0 0 6980100 471300 99800 0 0 460800 0 830 1550 1 11 71 17 0
1 0 0 6420500 1023800 100300 0 0 524288 0 855 1590 2 12 69 17 0
这次对块设备执行缓冲读取(buffered read),数据进入该设备的页缓存映射,而 /proc/meminfo 会把这些页面列入 Buffers,所以 buff 明显上升,cache 几乎不变。12
两个实验对比后,buff 和 cache 的分工就很清楚了:普通文件内容主要体现在 cache 中,块设备映射中的原始磁盘块则体现在 buff 中。实验数值不必与示例完全相同;后台服务、文件系统和内核版本都可能让其他字段同时发生变化。
常见误解:free 很低不是坏事
最后,再回到开头那个常见的疑问:free 很低,是否意味着内存不足?Linux 会利用空闲 RAM 进行缓存,因此 free 很低本身并不代表系统发生故障。当应用程序需要内存时,可以回收干净的文件缓存和一部分可回收的 slab 内存;dirty page 必须先写回,正在使用的 slab 对象,或者在未配置 swap 时无法换出的 shmem,则不一定能立即回收。4 10
判断内存是否紧张时,不要只看 free:
- 查看
free命令输出的available字段:它估算在不触发 swap 的情况下,还有多少内存可供新应用程序使用;计算时会考虑 Page Cache、可回收的 slab 内存和内存区域低水位线(zone low watermarks)。4 - 查看
vmstat的si/so:持续的换入和换出(swap in / out)是内存压力的重要信号;仅仅看到已经使用了一些 swap 空间,并不代表系统当前正在发生内存颠簸(thrashing)。17
总结
- 从一般概念来说,buffer 注重协调数据传输,cache 注重保留可复用数据;这不等于 Linux 中的“写入与读取”
- Linux 的
Cached主要反映 Page Cache,也包括 tmpfs / shmem;普通文件的缓冲读取和缓冲写入都会经过它 Buffers主要反映块设备映射中的原始磁盘块,以及部分文件系统使用的元数据块;inode / dentry 对象通常位于可回收的 slab 内存中- Page Cache 与 Buffer Cache 在 Linux 2.4 时期完成统一,现代系统中的主要文件缓存是 Page Cache
- 使用
vmstat 1对比“读取普通文件与读取原始块设备”,可以清楚看到cache与buff分别上升 free很低不等于内存不足,还需要进一步查看available和 swap 活动
参考资料
-
Stack Overflow — What is the difference between buffer and cache memory in Linux? ↩
-
Server Fault — In Linux, what is the difference between “buffers” and “cache” reported by the free command? ↩
-
Unix & Linux Stack Exchange — 30% of RAM is “buffers”. What is it? ↩
-
Linux 内核源码 — buffered block-device I/O and page-cache mapping / Buffers comes from nr_blockdev_pages() / nr_blockdev_pages() implementation ↩ ↩2
-
procps-ng 源码 — vmstat uses MEMINFO_MEM_CACHED_ALL / Cached + SReclaimable ↩
-
Fedora Magazine — System insights with command-line tools: free and vmstat ↩