人人都会AI编程

4.4 目录项缓存(dentry)与页缓存:加速文件访问的内核机制

更新时间:2026-07-12

每次在 Linux 中执行 cat /home/user/docs/notes.txt,看似简单的操作背后,其实隐藏着多次磁盘访问和路径解析。如果每一个环节都要重复读取磁盘,系统性能将大幅下降。Linux 内核正是通过 目录项缓存(dentry cache)页缓存(page cache) 这两大内存缓存机制,将最频繁使用的文件系统元数据和文件数据保存在 RAM 中,从而让文件访问快如闪电。

1. 目录项缓存:加速路径解析

当你输入一条包含多层目录的路径时,内核需要逐层解析:先从根 / 找到 home,再从 home 找到 user,依此类推。每一步都涉及读取磁盘上的目录文件,查找其中对应文件名到 inode 的映射。如果每次都从磁盘读取,速度会很慢。

目录项缓存(dentry cache) 就是用来记住这些“目录项对象”的内存结构。它的工作方式很直接:

  • 内核会为每一个最近访问过的文件名或目录名建立一个 dentry 对象,并将其缓存在内存中。
  • dentry 中保存了文件名、inode 号、父目录指针等信息。
  • 下一次再碰到相同的路径段时,内核就能直接从内存中找到 dentry 对象,跳过磁盘读取。

实用效果很明显:常用文件和目录的打开、查找操作几乎不受磁盘速度影响。你可以用 slabtop 命令观察 dentry 缓存占用的内存(dentry 项):

sudo slabtop -o | grep dentry

输出会显示当前 dentry 缓存的活跃对象数、对象大小等,帮助你了解系统正在缓存多少路径信息。

2. 页缓存:加速文件数据读写

页缓存(page cache)负责缓存 文件的实际内容数据。当进程读取文件的一部分时,内核首先检查这部分数据所在的“页”是否已在内存的页缓存中:

  • 如果在(缓存命中):直接将数据从内存复制给进程,无需访问磁盘。
  • 如果不在(缓存未命中):内核向磁盘发出读请求,将数据读入页缓存页,然后供给进程。此后该数据就驻留在内存中,后续再读就会命中。

写入操作同样受益于页缓存:进程写入的数据通常先写入页缓存中的页,暂时标记为“脏页”,由内核在合适的时机(如页回写线程 kswapdpdflush)异步刷新到磁盘。这称为 回写(write-back) 机制,能让应用不必等待磁盘 I/O 完成就可以继续执行。

页缓存的大小是动态的。Linux 通常会尽可能多地使用空闲内存来缓存文件数据,这就是为什么在 free -h 命令的输出中,“buff/cache” 一行会占据很大比例。不要因此以为内存不够用了——这些缓存内存随时可以在需要时被回收供应用程序使用。

free -h
# 关注 buff/cache 这一列,它包含页缓存和 dentry 缓存等。

3. 两者如何协同工作

假设你打开 /var/log/syslog 文件:

  1. 路径解析阶段,内核从根 dentry 开始,使用 dentry 缓存快速定位到 varlog,最终得到 syslog 的 inode。
  2. 得到 inode 后,内核可以获得文件的地址映射信息(数据块位置)。
  3. 当读取文件内容时,如果数据块已在页缓存中,则直接返回给进程;否则触发磁盘读取,填充页缓存。

整个过程中,dentry 缓存减少了“找到文件”的开销,页缓存减少了“读取文件内容”的开销。两者共同保证了高频文件访问的性能。

4. 实用的缓存管理

在真实运维中,你可能需要主动控制缓存行为:

  • 查看缓存压力与命中率:工具 vmstat 1sar -B 1 可以展示页缓存相关的统计(如 bibo 磁盘读写块数),帮助你判断缓存是否有效。
  • 手动释放缓存:在调试或测试 IO 性能时,你可能希望清空这些缓存以获得冷启动的真实测量值:
  # 释放页缓存
  echo 1 > /proc/sys/vm/drop_caches
  # 释放 dentry 和 inode 缓存
  echo 2 > /proc/sys/vm/drop_caches
  # 释放两者
  echo 3 > /proc/sys/vm/drop_caches
  

注意:这只是一个用于调试的手段,正常情况下无需手动执行,内核会根据内存压力自动回收。

  • 同步脏页到磁盘:为了安全起见,有时需要强制将页缓存中的脏数据刷写到磁盘:
  sync
  
  • 查看某个文件在缓存中的情况:你可能想确认某个大文件是否已经被部分缓存在内存中。可以使用 vmtouchfincore 等第三方工具。以 fincore 为例:
  fincore /var/log/syslog
  

会显示该文件有多少页(通常每页 4KB)当前处于内存的页缓存中。

5. 内存回收与权衡

dentry 缓存和页缓存都使用内核可回收的内存(MemAvailable 的一部分)。当系统内存紧张时,内核会自动回收这些缓存,优先保留应用程序所需的内存。回收策略是:

  • 干净的页(未被修改的页缓存)可以直接回收。
  • 脏页必须先写回磁盘才能回收,因此系统会限制脏页的总量和存在时间,避免一次性回收时产生 I/O 风暴。

你可以通过 sysctl 调整相关参数,例如:

# 设置脏页占总内存的最大百分比(默认为 10%)
vm.dirty_ratio = 10
# 设置长时间(百分秒)后必须写回
vm.dirty_expire_centisecs = 3000

这些参数在生产环境中可能用于优化存储子系统的行为,但通常默认值已经考虑周全。

总之,目录项缓存和页缓存是 Linux 文件系统高性能的两大支柱。理解它们能够帮助你正确解释 free 的内存使用、解决“内存被缓存占用”的疑惑,并在优化存储性能时做出明智的决策。