土法炼钢兴趣小组的算法知识备份

【操作系统百科】VFS 四层抽象

文章导航

分类入口
os
标签入口
#vfs#inode#dentry#dcache#superblock

目录

“一切皆文件” 不是口号。ext4、btrfs、procfs、sysfs、FUSE、cgroupfs、网络 socket 都在同一套 VFS(Virtual File System)接口下被读写。VFS 的核心做法只有两条:用四个对象 superblock / inode / dentry / file 表达层次关系,再用操作表(function pointer struct)让每种文件系统注入自己的实现。本文按这个骨架讲清楚结构、缓存与 RCU 快路径。

参考实现以 Linux 6.x 主线为准,文件路径默认指向 include/linux/fs.hinclude/linux/dcache.h

一、先看图

flowchart TD
    APP[用户 open/read/write] --> FD[struct file<br/>fd 表项]
    FD --> DENTRY[struct dentry<br/>目录项缓存]
    DENTRY --> INODE[struct inode<br/>元数据]
    INODE --> SB[struct super_block<br/>文件系统实例]
    SB --> FS[具体 FS 实现<br/>ext4/btrfs/xfs...]
    FD -.f_op.-> FOP[file_operations<br/>read/write/mmap...]
    INODE -.i_op.-> IOP[inode_operations<br/>lookup/create/link...]
    SB -.s_op.-> SOP[super_operations<br/>alloc_inode/sync_fs...]
    classDef vfs fill:#388bfd22,stroke:#388bfd,color:#adbac7;
    classDef ops fill:#a371f722,stroke:#a371f7,color:#adbac7;
    class FD,DENTRY,INODE,SB vfs
    class FOP,IOP,SOP ops

二、superblock

每个挂载的文件系统实例一个 super_block

struct super_block {
    struct list_head s_list;        // 全局链表
    dev_t s_dev;                    // 设备号
    unsigned long s_blocksize;
    struct file_system_type *s_type;
    const struct super_operations *s_op;
    struct dentry *s_root;          // 根 dentry
    struct hlist_bl_head s_roots;
    /* 其余字段省略 */
};

s_op 包含 alloc_inodedestroy_inodesync_fsstatfs 等。

三、inode

每个文件/目录/符号链接一个 inode(内存中的):

struct inode {
    umode_t i_mode;                 // 权限 + 文件类型
    kuid_t i_uid; kgid_t i_gid;
    loff_t i_size;
    struct timespec64 i_atime, i_mtime, i_ctime;
    const struct inode_operations *i_op;
    const struct file_operations *i_fop;
    struct super_block *i_sb;
    struct address_space *i_mapping;  // 页缓存
    unsigned long i_ino;
    /* 其余字段省略 */
};

inode 缓存(inode_cache)在 slab 里,hash 索引。

四、dentry

dentry 缓存路径组件 → inode 的映射:

struct dentry {
    struct dentry *d_parent;
    struct qstr d_name;             // 组件名:hash + len + name
    struct inode *d_inode;          // NULL = negative dentry
    const struct dentry_operations *d_op;
    struct super_block *d_sb;
    struct hlist_bl_node d_hash;    // dcache hash 桶
    struct list_head d_subdirs;
    /* 其余字段省略 */
};

dcache

全局 hash 表 dentry_hashtable:按 (parent, name_hash) 查。命中 dcache 的路径查找只需内存操作,不碰磁盘。

negative dentry

d_inode = NULL 表示”这个名字不存在”——缓存 ENOENT,避免重复磁盘查找。

cat /proc/sys/fs/dentry-state
# nr_dentry  nr_unused  age_limit  want_pages  nr_negative

五、struct file

open() 创建一个 struct file

struct file {
    struct path f_path;             // {vfsmount, dentry}
    struct inode *f_inode;
    const struct file_operations *f_op;
    atomic_long_t f_count;          // 引用计数
    unsigned int f_flags;           // O_RDONLY / O_WRONLY / O_RDWR ...
    fmode_t f_mode;
    loff_t f_pos;                   // 当前偏移
    struct address_space *f_mapping;
    /* 其余字段省略 */
};

file 是”打开实例”——同一 inode 可有多个 file(多次 open)。

六、操作表模式

VFS 的设计模式:每层一个操作表(函数指针结构体),具体 FS 填充实现。

// ext4 注册
static const struct inode_operations ext4_dir_inode_operations = {
    .create     = ext4_create,
    .lookup     = ext4_lookup,
    .link       = ext4_link,
    .mkdir      = ext4_mkdir,
    /* 其余成员省略 */
};

这是经典的”C 语言面向对象”。

七、mount 与 vfsmount

struct vfsmount {
    struct dentry *mnt_root;
    struct super_block *mnt_sb;
    int mnt_flags;
};

mount -t ext4 /dev/sda1 /mnt → 创建 super_block + vfsmount + 根 dentry。

mount namespace 隔离不同进程看到的挂载树。

八、RCU 路径查找

路径查找入口是 path_lookupat(),正常情况下先进入 rcu-walk 快路径(设置 LOOKUP_RCU):

  1. 全程不取 dentry 引用计数,也不加锁。
  2. RCU 读侧锁保护 dcache 链表的遍历。
  3. 每一步用 read_seqcount_retry(&dentry->d_seq, ...) 校验 dentry / inode 是否被并发修改。
  4. 一路成功 → 完成查找,整条路径几乎无原子操作开销。
  5. 任一步骤校验失败、需要 I/O、需要 ->d_revalidate 等慢路径 → 通过 unlazy_walk() 降级到 ref-walk 模式:取引用计数、清掉 LOOKUP_RCU,从该 dentry 开始按常规加锁方式继续。

这套设计让 stat() / access() / 重复打开热路径文件这类高频操作几乎只付出内存访问成本。详细的状态机和 mount 穿越在下一篇 路径名解析 展开。

九、观察

# dcache / inode cache 大小(按 slab cache 计)
cat /proc/slabinfo | grep -E 'dentry|inode_cache'

# 当前挂载
cat /proc/mounts

# 内核已注册的文件系统类型
cat /proc/filesystems

# 系统级打开文件数
cat /proc/sys/fs/file-nr
# 输出三列:allocated  unused  max

# negative dentry 计数等
cat /proc/sys/fs/dentry-state

十、小结


参考文献

工具


上一篇用户态分配器:jemalloc vs tcmalloc 下一篇路径名解析

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2025-08-22 · storage

【存储工程】文件系统基础:inode、目录与 VFS

磁盘是一个线性的块数组,但没有人愿意用"第 48372 号扇区"来定位自己的文档。文件系统(File System)就是那个把"名字"映射到"数据"的翻译层——它把一片平坦的块空间组织成人类可以理解的层级结构,同时维护着每个文件的权限、大小、时间戳等元数据(Metadata)。

2026-05-16 · os

【操作系统百科】VFS I/O 路径全景

一次 read(fd, buf, n) 从用户态到磁盘要穿过多少层?本文追踪 VFS read/write 全路径——file_operations、iter_iov、iomap、页缓存命中与 miss、直接 I/O 旁路、块层提交、fsnotify 插桩。

2026-05-06 · os

【操作系统百科】内核内存调试

内核内存 bug 是最难追的:UAF、OOB、double free、leak 都可能沉默数月。本文讲 KASAN 三种模式、KFENCE 生产采样、kmemleak、SLUB_DEBUG、UBSAN/KCSAN 联动。

2026-04-22 · os

操作系统百科

Linux 6.x 视角下的操作系统系列索引:110 篇覆盖调度、虚拟内存、文件系统与 I/O、并发、隔离、可观测性,按主题、阅读路径与关键问题三种入口组织。


By .