buffer和cache
Linux Buffer 与 Cache 原理及观测完整文档
1. 文档目标
本文档面向有一定 Linux 运维、内核或性能调优经验的工程人员,系统性说明:
-
Buffer 与 Cache 的内核语义与历史背景
-
二者在现代 Linux 内核中的统一实现方式
-
内存回收、脏页与写回机制
-
工程级观测方法(free / meminfo / vmstat / eBPF 工具)
-
典型性能问题定位路径
目标不是概念普及,而是帮助在真实系统中正确判断内存与 I/O 行为。
2. Linux 内存设计总览
Linux 的核心内存设计原则是:
空闲内存是浪费的内存。
因此:
-
内存会被尽可能用于缓存
-
Cache 与 Buffer 属于可回收内存
-
内存紧张时,内核会自动回收它们
在现代内核中,Cache / Buffer / Slab 均参与统一的回收体系(LRU + reclaim)。
3. Buffer 的定义与实现
3.1 逻辑定义
Buffer(Buffer Cache) 用于缓存:
-
块设备(block device)上的数据块
-
文件系统元数据(inode、superblock、bitmap)
-
Journal / raw block I/O
它的关注点是:
-
“块”
-
“设备”
-
“文件系统结构”
3.2 内核实现要点
-
实际内存载体仍然是
struct page -
通过
struct buffer_head描述 page 中的 block -
buffer 并不拥有独立内存池
关键结论:
Buffer 只是 Page Cache 的一种使用方式
4. Cache 的定义与实现
4.1 逻辑定义
通常所说的 Cache 指:
-
Page Cache
-
文件内容缓存(file-backed pages)
服务对象:
-
普通文件
-
可执行文件
-
动态库
-
mmap 文件
4.2 读写路径
-
read():
-
命中 → 直接内存拷贝
-
未命中 → 磁盘 I/O → 填充 page cache
-
-
write():
-
写入 page cache
-
标记 dirty
-
延迟写回磁盘
-
4.3 mmap 行为
-
mmap 文件直接映射 page cache
-
修改 mmap 区域等价于修改 page cache
-
回写由内核统一调度
5. Buffer 与 Cache 的关系
| 维度 | Buffer | Cache |
|---|---|---|
| 面向对象 | 块设备 | 文件 |
| 使用场景 | 元数据 / raw I/O | 文件内容 |
| 是否独立内存 | 否 | 否 |
| 是否可回收 | 是 | 是 |
| 内核载体 | page + buffer_head | page |
核心结论:
二者在物理内存层面没有边界,仅是统计与语义区分。
6. 低阶内存与高阶内存(LowMem / HighMem)
6.1 背景与历史意义
低阶内存与高阶内存的划分,源自 32 位 Linux 内核的虚拟地址空间限制,但其思想对理解现代内核内存布局、ZONE 划分、内存回收路径仍然非常重要。
在 32 位系统中:
-
内核虚拟地址空间通常只有 1GB(3G/1G 模型)
-
并非所有物理内存都能被内核永久映射
因此引入了 High Memory(高端内存) 的概念。
6.2 低阶内存(Low Memory)
低阶内存指:
-
始终被内核线性映射
-
可以直接通过虚拟地址访问
在 x86 32 位中通常包括:
-
ZONE_DMA
-
ZONE_NORMAL
特性:
-
内核代码、内核数据结构必须位于低阶内存
-
slab、page cache、buffer cache 的核心元数据依赖低阶内存
-
低阶内存耗尽会直接导致系统不稳定
6.3 高阶内存(High Memory)
高阶内存指:
-
不被永久映射到内核地址空间
-
需要通过临时映射(kmap / kmap_atomic)访问
在 x86 32 位中:
-
ZONE_HIGHMEM
特点:
-
主要用于存放用户态数据页
-
Page Cache 的数据页大量来自高阶内存
-
访问成本更高
6.4 HighMem 与 Buffer / Cache 的关系
关键点:
-
Page Cache 的 数据页 可以位于 HighMem
-
Page Cache 的 元数据(struct page、radix tree)必须位于 LowMem
-
Buffer_head 本身位于 LowMem
因此会出现经典现象:
HighMem 充足,但 LowMem 耗尽,系统仍然 OOM
6.5 低阶内存耗尽的典型症状
-
dmesg 中出现:
-
"Low memory"
-
"Out of memory"
-
-
slabtop 显示 slab 对象异常增大
-
Page Cache 很大,但内核分配失败
6.6 观测低阶 / 高阶内存
6.6.1 /proc/meminfo
关键字段:
-
LowTotal / LowFree
-
HighTotal / HighFree
-
DMA / DMA32 / Normal
6.6.2 /proc/zoneinfo
用于精确分析各 ZONE:
-
free pages
-
watermarks
-
reclaim 行为
6.7 在 64 位系统中的变化
在 64 位 Linux 中:
-
地址空间充足
-
不再区分传统意义上的 HighMem
-
ZONE_HIGHMEM 通常不存在
但以下问题仍然存在:
-
ZONE_DMA / DMA32 限制
-
内核虚拟地址碎片
-
高阶页(order > 0)分配失败
7. 脏页(Dirty Page)与写回机制
6.1 Dirty Page
-
被修改但尚未写入磁盘的 page
-
包含 buffer 与 cache 产生的脏页
6.2 写回控制参数
-
vm.dirty_ratio
-
vm.dirty_background_ratio
-
vm.dirty_expire_centisecs
-
vm.dirty_writeback_centisecs
6.3 回收优先级(简化)
-
Page cache(clean)
-
Page cache(dirty → writeback)
-
Slab reclaimable
-
Swap
7. 基础观测工具
7.1 free
-
buff/cache:buffer + cache
-
available:判断是否内存紧张的关键指标
7.2 /proc/meminfo
关键字段:
-
Buffers
-
Cached
-
SReclaimable
-
Dirty
-
Writeback
这是最权威的数据来源。
7.3 vmstat
重点关注:
-
si / so(swap)
-
wa(I/O wait)
-
kswapd 活跃度
8. cachetop:Page Cache 命中率观测
8.1 工具定位
cachetop 是基于 eBPF 的诊断工具,用于统计:
-
Page Cache 命中(hits)
-
未命中(misses)
-
脏页产生(dirties)
-
读命中率(READ_HIT%)
它回答的问题是:
Cache 是否真的提升了性能。
8.2 统计原理
通过 hook 内核路径:
-
mark_page_accessed
-
add_to_page_cache_lru
-
account_page_dirtied
因此反映真实 I/O 行为。
8.3 工程判断经验
| READ_HIT% | 判断 |
|---|---|
| > 99% | 非常健康 |
| 95–99% | 正常 |
| 90–95% | 需关注 |
| < 90% | 明显 I/O 风险 |
9. cachestat / filetop / vfsstat
9.1 cachestat
-
系统级 cache 行为
-
判断整体 cache 效率
9.2 filetop
-
文件维度读写统计
-
定位 cache 污染源
9.3 vfsstat
-
VFS 层调用统计
-
辅助判断 metadata / buffer 压力
10. Buffer 行为的工程观测
Buffer 通常隐藏在:
-
文件系统 metadata
-
journal 写入
表现为:
-
Buffers 偏高
-
bi/bo 增大
-
journal I/O 活跃
需结合:
-
/proc/meminfo
-
iostat -x
11. 典型问题与定位路径
11.1 Cache 很大但性能差
-
free:cache 大
-
vmstat:wa 高
-
cachetop:READ_HIT% 低
结论:
-
Cache 被顺序 I/O 冲刷
-
工作集超出内存
11.2 单进程污染 Cache
-
cachetop:单 PID miss 极高
-
filetop:大文件顺序读
对策:
-
O_DIRECT
-
posix_fadvise(DONTNEED)
12. 常见误区澄清
-
Cache 多 ≠ 内存不足
-
free 少 ≠ 内存压力
-
Buffer/Cache 不会抢应用内存
13. 工程级总结
-
Buffer 与 Cache 本质统一于 Page Cache
-
关键不是“占用多少”,而是“命中率如何”
-
cachetop 是判断 cache 是否有效的核心工具
-
真正的内存压力指标是:
-
available
-
swap
-
kswapd
-
I/O wait
-
14. /proc 与 cgroup 视角的内存观测(核心补充)
本章节从 内核真实数据源 的角度,补充说明内存、cache、buffer、slab 在 procfs 与 cgroup 中的表现与差异。这是生产排障中不可缺失的一层。
14.1 MEMINFO:/proc/meminfo(全局内存事实源)
/proc/meminfo 是 Linux 内存统计的唯一权威来源,free/top/ps 均基于它派生。
14.1.1 与 Cache / Buffer / Slab 直接相关字段
-
MemTotal / MemFree
-
Buffers
-
Cached
-
SReclaimable
-
Slab
-
Dirty / Writeback
-
LowTotal / LowFree(32 位或开启 HighMem)
-
HighTotal / HighFree
关系说明:
-
Cached:文件页 page cache(不含 swap cache)
-
Buffers:block buffer(metadata / raw I/O)
-
Slab = SReclaimable + SUnreclaim
-
available = MemFree + 可回收 cache + SReclaimable
关键工程结论:
判断内存是否紧张,永远不要只看 MemFree。
14.2 SLABINFO:/proc/slabinfo(内核对象内存)
Slab 是 低阶内存的主要消耗者之一,在 cache / buffer / VFS / 网络栈 中无处不在。
14.2.1 slab 的构成
-
inode_cache
-
dentry
-
buffer_head
-
kmalloc-*
-
task_struct
这些对象:
-
全部位于 LowMem
-
大量存在会直接挤压 page cache 元数据空间
14.2.2 slabinfo 的工程价值
-
发现 slab 泄漏
-
解释 LowMem 耗尽但 HighMem 充足的问题
-
定位 VFS / 网络 / buffer_head 异常增长
通常配合:
-
slabtop
-
/proc/meminfo 中的 Slab / SReclaimable
14.3 CGROUP V1:/sys/fs/cgroup/memory
在 cgroup v1 中,page cache 计入 memory.limit,这是大量“容器内存误判”的根源。
14.3.1 关键文件
-
memory.usage_in_bytes
-
memory.limit_in_bytes
-
memory.stat
memory.stat 中重要字段:
-
cache
-
rss
-
mapped_file
-
inactive_file / active_file
-
inactive_anon / active_anon
工程结论:
-
cache 与 rss 共享内存上限
-
cache 压缩会直接影响应用性能
-
drop_caches 在容器内可能被误用
14.4 CGROUP ROOT:/sys/fs/cgroup(v2 统一层级)
cgroup v2 采用 统一内存控制模型,但 cache 统计更加复杂。
14.4.1 关键路径
-
memory.current
-
memory.max
-
memory.stat
-
memory.events
memory.stat 中:
-
file
-
anon
-
slab
-
slab_reclaimable
-
slab_unreclaimable
关键区别:
-
slab 被显式计入 cgroup
-
file cache 与 anon 明确区分
-
OOM 与 reclaim 事件可观测
14.5 Slab 在 cgroup 中的表现
-
cgroup v1:slab 不完全计入(历史问题)
-
cgroup v2:slab 明确计入 memory.max
工程影响:
-
大量 dentry / inode 会导致容器 OOM
-
但 free/top 在 host 上看似正常
14.6 常见“内存看不懂”的真实原因
-
meminfo 看的是 全局
-
cgroup 看的是 配额内
-
slab 占用的是 LowMem
-
cache 在 cgroup 中是 受限资源
15. 适用场景
-
性能压测分析
-
I/O 瓶颈定位
-
数据库 / 大数据 / 容器环境
-
内核行为理解与调优
浙公网安备 33010602011771号