Linux VFS 完整篇:从path_openat、dentry/inode 到page cache 与挂载排障

发布时间:2026/9/5 10:46:32
Linux VFS 完整篇:从path_openat、dentry/inode 到page cache 与挂载排障 open返回诡异 errno、海量小文件把内存打满、O_DIRECT对齐失败、容器 overlay 写放大被误判成「磁盘慢」——根因多半落在VFS 对象模型与挂载/缓存策略而不是某一个具体 FS 的业务逻辑。本文把路径查找、打开绑定、读写与 page cache、挂载选项与观测命令合成一篇闭环便于对照源码与strace/findmnt验证。源码锚点路径作用fs/namei.cpath_openat、link_path_walk、lookup 快/慢路径fs/open.cdo_sys_open/do_filp_open、vfs_openfs/read_write.cvfs_read/vfs_write、vfs_iter_*fs/dcache.cdentry 缓存、negative dentry、回收协作fs/inode.cinode 生命周期、权限相关入口fs/super.csuper_block、挂载/卸载协作mm/filemap.cpage cachefilemap_read/generic_file_*include/linux/fs.hinode、file、file_operations、super_operationsinclude/linux/dcache.hstruct dentryDocumentation/filesystems/vfs.rstVFS 方法表总览打开后用户态 fd 对应的内核对象/* include/linux/fs.h */structfile{conststructfile_operations*f_op;structinode*f_inode;void*private_data;loff_tf_pos;/* … */};structfile_operations{structmodule*owner;ssize_t(*read)(structfile*,char__user*,size_t,loff_t*);ssize_t(*write)(structfile*,constchar__user*,size_t,loff_t*);ssize_t(*read_iter)(structkiocb*,structiov_iter*);int(*open)(structinode*,structfile*);/* … */};路径打开逻辑骨架版本细节以树内符号为准/* 用户态 openat → 系统调用 → fs/namei.c / fs/open.c */path_openat(...)→ link_path_walk/* 逐组件 */→lookup_fast(dcache)或lookup_slow(→ inode-i_op-lookup)→ 处理 O_CREAT/O_TRUNC/尾随斜线等 → vfs_open → file-f_opfops_get(inode-i_fop)→ f_op-open(inode,file)/* 具体 FS 或设备 fops */调用链打开与读写主路径是否命中未命中openat(dfd, path, flags)do_sys_open / do_filp_openpath_openatlink_path_walkdcache 命中?lookup_fastlookup_slow → i_op-lookup权限 / 标志处理vfs_open绑定 file-f_op inode-i_fopf_op-openread → vfs_read / read_iterpage cache?直接拷贝用户态generic_file_* → FS → 块层对象分层与缓存协作缓存与后端VFS对象用户态进程 fd 表struct filestruct dentrystruct inodestruct super_blockdcachepage cache / i_mapping具体 FS: ext4/XFS/...块层 bio重点知识1. 四件套分工先分清再谈「慢」对象职责排障信号dentry路径组件缓存含 negative找不到路径、海量小文件内存涨inode元数据 i_op/i_fopi_mapping权限/大小/时间戳异常file一次打开会话位置、模式、private_data打开后读写/ioctl 失败super_block一个挂载实例的 FS 全局状态挂载只读、配额、冻结故障先归类路径找不到dentry/权限还是打开后读写失败f_op/块层避免一上来改 FS 调优参数。2.file-f_op在 open 时钉死后续read/write/ioctl都走这张表。设备节点会在字符/块层替换 fops如chrdev_open普通文件则是具体 FS 的file_operations。挂错类型、overlay 层错乱、错误的 inode都会进错操作集。3. dcache、negative dentry 与内存压力海量小文件遍历会堆 dentry/inodevm.vfs_cache_pressure影响回收积极性。negative dentry 加速「反复查不存在路径」也能在并发创建场景下制造短暂「看不见刚创建的文件」的错觉——结合业务与strace看是 ENOENT 还是竞态。4. page cache、回写与O_DIRECT缓冲 I/O命中i_mapping则少读盘脏页经 writeback 回盘。O_DIRECT绕过 page cache偏移/长度需按逻辑块对齐否则常见EINVAL。观测脏页与回写/proc/meminfo的Dirty/Writeback配合iostat -x。5. 挂载选项与容器场景findmnt-T/path/to/filemount|grepmp# 常见noatime 降元数据写ro/nodev/nosuid 安全边界cat/proc/sys/vm/vfs_cache_pressure场景建议常见坑数据库数据目录单独挂载、评估noatimeatime 更新放大写容器 rootfs理解 overlay 上层写放大误判底层磁盘慢SSDdiscard/fstrim策略明确盲目 discard 拖延迟只读根ro 可写目录分离应用写日志失败6. 挂载与超级块VFS 的另一半入口打开路径管「文件」挂载路径管「一棵树挂到哪里」。用户态mount最终落到 VFS 的挂载点与super_block装配类型、标志MS_RDONLY等、选项字符串交给具体 FS 的fill_super/get_tree一类入口现代用 fs_context。排障时findmnt -o TARGET,SOURCE,FSTYPE,OPTIONS -T path看实际生效选项只读根上写失败先看挂载 flags再看应用路径是否写到只读层绑定挂载/rbind容易造成「同一 inode 多个路径」的认知错乱删文件前先findmnt。7. 写路径与回写和读对称write → vfs_write / write_iter → 缓冲写标记 page dirty → 稍后 writeback → FS → bio → O_SYNC/fdatasync在返回前推进完整性语义细节随 FS脏页过高时应用线程可能被卡住参与回写——表象是「业务变慢」根因在写放大或落盘瓶颈。配合Dirty/Writeback与 FS journal 模式如 ext4 dataordered一起看。8. 观测与排障命令strace-eopenat,open,stat,read,writecat/path/to/filecat/proc/meminfo|grep-ECached|Dirty|Writebackiostat-x1slabtop-o|head# 关注 dentry/inode 相关 slab# 权限与挂载namei-l/path/to/filels-ld$(dirname/path/to/file)现象优先怀疑核对点ENOENT/EACCES路径/权限/挂载namei、findmntEINVALon direct I/O对齐块大小、缓冲地址内存持续升高dentry/inode/page cacheslabtop、vfs_cache_pressure写延迟尖刺脏页回写/日志Dirty、journal、iostat容器内写爆盘overlay 上层df分层、docker system df等Checklist能口述path_openat→ dentry lookup →vfs_open→f_op-open的链分清故障在路径查找、权限、还是f_op/块层strace的 open 标志与 errno 能对应到 VFS 分支理解 dentry/inode/file/super_block 各自职责海量小文件场景评估过vfs_cache_pressure与目录布局O_DIRECT/挂载选项noatime等按业务验证而非照抄博客容器 overlay 写放大与底层磁盘延迟能区分排查小结VFS 的设计意图是用统一对象模型让 ext4/XFS/NFS/proc 共享系统调用入口。排障按「路径 → 绑定 f_op → 缓存/块层 → 挂载选项」分层推进调优先改可观测、可回滚的挂载与压力参数再碰具体 FS 内部。