【Note】《深入理解Linux内核》 Chapter 15 :深入理解 Linux 页缓存

《深入理解Linux内核》 Chapter 15 :深入理解 Linux 页缓存

关键词:页缓存、address_space、radix tree、page、writeback、dirty page、mmap、文件系统缓存、文件 I/O 性能优化、direct I/O


一、页缓存是什么?为什么重要?

1.1 定义

页缓存(page cache)是 Linux 内核用于缓存文件内容的内存区域,避免每次文件读写都访问磁盘。

1.2 页缓存的核心目的

  • 降低 I/O 延迟;
  • 提高磁盘访问效率;
  • 减少重复读取;
  • 支持延迟写回(writeback);
  • 提供内存映射(mmap)机制。

1.3 页缓存的表现形式

  • 文件通过 read()write()mmap() 访问;
  • 文件数据页在页缓存中存在;
  • 数据在页缓存中被标记为 clean(干净)或 dirty(脏页);
  • 脏页会被后台线程回写到磁盘。

二、页缓存的关键数据结构

页缓存建立在以下三个核心结构之上:

2.1 struct page

表示系统中的一页物理内存,页缓存的基本单位:

c 复制代码
struct page {
    unsigned long flags;
    atomic_t _count;
    void *mapping;
    pgoff_t index;
    ...
};
  • flags:标志位(如 PG_dirty、PG_locked);
  • mapping:指向所属的 address_space;
  • index:页在文件中的偏移页号(单位为页)。

2.2 struct address_space

每个文件都有一个 address_space 结构,管理它的所有缓存页:

c 复制代码
struct address_space {
    struct inode *host;
    struct radix_tree_root page_tree;
    unsigned long nrpages;
    const struct address_space_operations *a_ops;
    ...
};
  • page_tree:以页号为 key 的 radix tree(基数树),实现高效查找;
  • a_ops:操作方法集(如 readpage、writepage)。

2.3 struct address_space_operations

为页缓存提供访问操作的接口:

c 复制代码
struct address_space_operations {
    int (*readpage)(struct file *, struct page *);
    int (*writepage)(struct page *, struct writeback_control *);
    ...
};
  • 由具体文件系统(如 ext4)实现;
  • 页缓存管理不直接读写磁盘,而是调用这些函数。

三、页缓存生命周期与文件访问流程

3.1 读流程(read)

c 复制代码
sys_read()
 └── vfs_read()
     └── generic_file_read_iter()
         └── filemap_read()
             └── pagecache_get_page()  // 查找或分配页
             └── ->a_ops->readpage()  // 页面未命中时触发

流程说明:

  1. 读取页缓存(radix tree 查询);
  2. 若不存在,则通过 readpage 填充;
  3. 将页内容复制到用户空间缓冲区。

3.2 写流程(write)

c 复制代码
sys_write()
 └── vfs_write()
     └── generic_file_write_iter()
         └── filemap_write()
             └── pagecache_get_page()  // 查找页
             └── copy_from_user()      // 写入页缓存
             └── set_page_dirty()

流程说明:

  1. 查找目标页;
  2. 若页不存在则分配;
  3. 将用户数据拷贝入页;
  4. 将页标记为 dirty。

四、脏页管理与回写机制(Writeback)

4.1 脏页定义

修改后的页被标记为 dirty(PG_dirty),需要回写到磁盘以保证数据一致性。

4.2 回写路径

由后台线程或显式触发:

c 复制代码
flush_dirty_pages()
 └── writeback_inodes()
     └── ->a_ops->writepage()

常见触发方式:

  • 系统空闲时;
  • 调用 sync()fsync()
  • 达到内存压力阈值;
  • 定期写回机制(dirty_expire_centisecs)。

4.3 脏页控制参数

  • /proc/sys/vm/dirty_ratio:最大脏页占比;
  • /proc/sys/vm/dirty_background_ratio:达到该比例时触发后台写回;
  • /proc/sys/vm/dirty_writeback_centisecs:后台写回周期;
  • /proc/sys/vm/dirty_expire_centisecs:判断页是否过期。

五、内存映射 I/O(mmap)与页缓存

5.1 mmap 简介

将文件内容直接映射到用户地址空间,实现高效访问:

c 复制代码
void *addr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

5.2 mmap 与页缓存的关联

  • mmap 会将页缓存页直接映射到用户地址空间;
  • 读写数据不再经过 read()/write(),而是直接访问页;
  • 修改后页也会被标记为 dirty。

5.3 写回机制

  • msync():手动同步映射数据到磁盘;
  • munmap():取消映射可能触发写回;
  • madvise():优化页面使用行为(如提前释放)。

六、页缓存与块设备/文件系统协作

6.1 页缓存在文件系统中的地位

  • 页缓存维护每个 inode 的缓存;
  • 使用 address_space 统一访问接口;
  • 文件系统提供底层磁盘操作方法。

6.2 文件系统对接方式

  • 负责实现 readpage() / writepage() 等;
  • 例如 ext4 在 fs/ext4/inode.c 中实现对应方法;
  • 页缓存自动调度,文件系统实现数据同步。

七、直接 I/O(Direct I/O)绕过页缓存

7.1 定义

某些场景(数据库、日志)不希望缓存数据,直接对磁盘读写,称为 Direct I/O。

7.2 使用方式

c 复制代码
open("file", O_DIRECT);

7.3 特性

  • 绕过页缓存;
  • 用户缓冲区需对齐;
  • 对性能敏感程序更有用;
  • 但不再享受缓存带来的性能提升。

八、页缓存回收与清理

8.1 页回收机制

  • Linux 使用 LRU(最近最少使用)算法维护页状态;
  • 分为 active/inactive 两级列表;
  • 页面在使用中被提升,空闲时被回收。

8.2 清理命令与技巧

  • sync:触发全局写回;
  • echo 3 > /proc/sys/vm/drop_caches:丢弃页缓存、目录项、inode 缓存;
  • vmstat / top / free:查看内存使用情况;
  • perf record -e 跟踪 writeback 情况。

九、写时复制(COW)机制与共享页

  • 多个进程通过 mmap 映射同一文件时可共享页;
  • 若进程写入,触发 COW 分离复制;
  • 提高效率、减少内存占用。

十、页缓存与性能优化建议

  • 利用缓存延迟写,减少同步阻塞;
  • 使用 O_DIRECT 提高高吞吐任务性能;
  • 合理配置 dirty_* 参数减少卡顿;
  • 定期回写可避免突然 IO 峰值。

十一、源码路径参考

路径 说明
mm/filemap.c 页缓存读取与写入核心逻辑
mm/page-writeback.c 回写机制实现
fs/buffer.c 文件系统缓存页管理
include/linux/page-flags.h 页状态标志位
include/linux/address_space.h address_space 定义

十二、小结

  • 页缓存是提升 Linux 文件访问性能的核心机制;
  • 通过 address_space 管理每个 inode 的页面;
  • 读写系统调用和 mmap 都利用页缓存;
  • 写回机制由后台线程或显式触发;
  • 文件系统负责底层 IO,页缓存提供统一访问层;
  • 参数调优可显著提升 IO 表现。
相关推荐
small_wh1te_coder1 分钟前
硬件嵌入式学习路线大总结(一):C语言与linux。内功心法——从入门到精通,彻底打通你的任督二脉!
linux·c语言·汇编·嵌入式硬件·算法·c
小张是铁粉8 分钟前
docker在Linux的安装遇到的问题
linux·docker·容器
weixin_77143231111 分钟前
linux系统 weblogic10.3.6(jar) 下载及安装
linux·运维·jar
绝不偷吃20 分钟前
FastDFS分布式储存
linux·nginx
IC 见路不走2 小时前
LeetCode 第91题:解码方法
linux·运维·服务器
翻滚吧键盘2 小时前
查看linux中steam游戏的兼容性
linux·运维·游戏
小能喵2 小时前
Kali Linux Wifi 伪造热点
linux·安全·kali·kali linux
汀沿河2 小时前
8.1 prefix Tunning与Prompt Tunning模型微调方法
linux·运维·服务器·人工智能
zly35003 小时前
centos7 ping127.0.0.1不通
linux·运维·服务器
小哥山水之间3 小时前
基于dropbear实现嵌入式系统ssh服务端与客户端完整交互
linux