📘 推荐阅读
- 《Yocto项目实战教程:高效定制嵌入式Linux系统》
京东购买链接
内存管理中的缓存分类与分页机制关系 —— 实战代码与现象解读
一、Linux系统中常见的“缓存”类型
1. CPU Cache(L1/L2/L3)
- 作用:CPU 内部缓存,非操作系统可控。理解时可跳过,不归 OS 内存管理范畴。
2. Page Cache(页缓存)
-
用途:文件/块设备I/O加速,占用大量物理内存。
-
现象:读/写大文件,free 命令
buff/cache
显著增长。 -
实战命令:
# 查看 buff/cache 变化 free -h # 查看详细内存类型分布 cat /proc/meminfo | grep -E 'Cached|Buffers'
-
典型代码(读文件触发 Page Cache):
// 文件内容被 Page Cache 缓存 FILE *fp = fopen("/tmp/largefile", "r"); char buf[4096]; while (fread(buf, 1, sizeof(buf), fp) > 0) { /* 触发页缓存 */ } fclose(fp);
- 系统通过 page cache 把磁盘内容缓存在物理内存页,减少重复 IO。
3. Buffer Cache
- 用途:块设备元数据缓冲,现代 Linux 已合并到 Page Cache。
- 代码说明:无需额外理解,实际调优和分析时关注 Page Cache 即可。
4. Slab/Slub Cache(内核对象缓存池)
-
用途:内核频繁分配的小对象(如 task_struct、inode、dentry)的高效缓存。
-
典型现象:dentry/inode 缓存高峰时,占用大量物理内存。
-
内核代码片段(内核对象 slab 分配):
struct task_struct *tsk = kmem_cache_alloc(task_struct_cachep, GFP_KERNEL); // ... do work ... kmem_cache_free(task_struct_cachep, tsk);
-
实用命令:
# slabtop 工具查看 Slab/Slub cache 占用 slabtop
5. 用户态程序的 malloc/new 缓存
-
用途:应用程序分配的内存,最终通过系统调用由内核分配物理页。
-
示例代码:
char *buf = malloc(1024*1024); // 用户空间分配 strcpy(buf, "hello world"); free(buf);
- 系统后台通过 brk/mmap 实现,物理页分配与分页机制密切相关。
二、分页机制与缓存的本质联系(代码+流程)
1. 分页机制(Paging)简述
-
作用:操作系统用页(Page,通常4K)为单位管理物理内存。
-
核心流程(伪代码):
// 进程访问虚拟地址,内核页表转换 physical_page = mmu_lookup(virtual_address, current->mm->page_table);
2. 物理页的“用途分配”核心图解
[ 物理内存 RAM ]
+-----------------------------------+
| Page0 | Page1 | ... | PageN |
+-----------------------------------+
↓ ↓ ↓
+------+ +--------+ +---------+
| Slab | | Page | | 用户堆 |
|Cache | | Cache | | (malloc)|
+------+ +--------+ +---------+
(内核对象) (文件缓存) (应用数据)
- 所有缓存、堆、栈,底层全是“物理页”,通过分页机制统一分配和管理。
3. Page Cache 分配流程(内核源码片段)
// 伪代码:读文件,未命中 page cache,则分配新页
struct page *page = find_get_page(mapping, index);
if (!page) {
page = alloc_page(GFP_KERNEL); // 分配物理页
add_to_page_cache_lru(page, mapping, index, GFP_KERNEL);
// ...读取磁盘内容到page...
}
三、工程实践与典型现象(现象-代码-分析闭环)
1. 缓存占用分析命令
# free/top 等都能直观看到 buff/cache
free -h
top
# /proc/meminfo 详细统计(关键行)
cat /proc/meminfo | grep -E 'Buffers|Cached|Slab'
Buffers
、Cached
、Slab
分别对应块缓存、页缓存、slab对象池缓存。
2. 缓存释放与压力测试(代码+现象)
-
编写应用,持续读写大文件,观察 Page Cache 激增
FILE *fp = fopen("/tmp/bigfile", "w"); for (int i = 0; i < 1024*1024; ++i) fwrite("abcdefghij", 1, 10, fp); fclose(fp); // 此时 free -h 可见 buff/cache 大增
-
手动释放缓存页(慎用,示例)
# 释放Page Cache sync; echo 1 > /proc/sys/vm/drop_caches # 释放Page+inode/dentry sync; echo 3 > /proc/sys/vm/drop_caches
3. Slab Cache 高峰实战
-
触发 inode/dentry 大量分配
find /
、ls -lR /
这类操作会暴增 Slab Cache。slabtop
实时查看对象池缓存波动。
四、知识总结:分页机制与缓存的核心逻辑
1. 逻辑关系归纳
- 分页机制 = 分配和追踪物理页的底层手段。
- 各种缓存 = 被分配出来物理页的用途之一。
- 管理手段 = 内核通过页表、slab管理器、LRU等算法动态分配、回收页。
2. 关键代码链路梳理
- 用户 malloc → 内核
brk/mmap
→ 页表 → 物理页分配 - 文件读写 → Page Cache 查询/分配 → 物理页 → 页表映射
- 内核对象分配 → Slab Cache 管理 → 物理页
五、经典自测问题(工程导向)
- Page Cache 与 Slab Cache 在分配/回收时的核心区别与联系是什么?
(可通过slabtop
/free
命令和实际代码实验观察) - 分页机制对用户态和内核态分配有何统一与差异?
(brk、mmap、kmalloc、alloc_page 这些API关系) - 内存紧张时,Linux 怎么优先回收缓存页?回收机制在什么场景下会失效?
(结合 drop_caches/kswapd 行为分析)
六、口诀与结语
“缓存其实就是物理页,分页机制是根本。代码验证多实验,现象理解才深刻。”
📘 推荐阅读
- 《Yocto项目实战教程:高效定制嵌入式Linux系统》
京东购买链接