当前位置:首页>Linux>Linux VFS 架构总览:从“文件”到“虚拟文件系统”的全链路解析

Linux VFS 架构总览:从“文件”到“虚拟文件系统”的全链路解析

  • 2026-10-13 13:22:40
Linux VFS 架构总览:从“文件”到“虚拟文件系统”的全链路解析

如果你在 Linux 下运行任何应用,你一定在使用文件系统。

文件系统的核心能力并不只是“存文件”,更重要的是它为应用提供了统一的存储接口:

  • 读写文件

  • 创建/删除/重命名

  • 目录遍历

  • 权限控制

  • 软硬链接

  • 内存映射

  • 设备访问

  • 网络文件访问

这些看似简单的操作,背后是复杂的内核体系。

VFS 层结构示意

┌────────────────────────────────────┐│             VFS 层                ││                                    ││ sys_open/read/write/...            ││      │                             ││      ▼                             ││ path_lookup / generic_file_*       ││      │                             ││      ▼                             ││ dentry/inode/page cache            ││      │                             ││      ▼                             ││ filesystem specific ops (ext4)     │└────────────────────────────────────┘

Linux 的文件系统并不是单一的,而是多种多样的:ext4、xfs、btrfs、nfs、cifs、tmpfs、procfs、sysfs、overlayfs 等。

如果没有一层统一抽象,内核将变成“各种文件系统各自实现一套系统调用”的混乱系统。

因此,Linux 引入了 VFS(Virtual File System):

它不是一个真正的文件系统,而是一层 统一的抽象层,用于连接系统调用与具体文件系统。


1. 为什么要有 VFS?

1.1 多文件系统并存的现实需求

Linux 需要支持:

  • 本地磁盘文件系统(ext4、xfs、btrfs)

  • 网络文件系统(NFS、CIFS)

  • 内存文件系统(tmpfs)

  • 特殊虚拟文件系统(procfs、sysfs)

  • 可插拔的文件系统(FUSE)

如果没有统一抽象,用户态程序需要针对不同文件系统编写不同代码,这显然不可行。

1.2 系统调用与文件系统实现解耦

系统调用层(如 open、read、write)不应该关心底层存储细节。

VFS 的作用是:

  • 将系统调用统一映射到 VFS 操作

  • VFS 再通过函数指针调用具体文件系统实现

  • 用户态程序无需关心底层文件系统类型


2. VFS 的核心目标与设计原则

2.1 统一接口

VFS 为每个文件系统定义了一套统一的操作集:

    super_operationsinode_operationsfile_operationsdentry_operations

    每个文件系统只要实现这些操作,就能被 VFS 识别与调用。

    2.2 统一命名空间(Namespace)

    Linux 的文件系统结构是一棵树:

    /|-- bin|-- etc|-- home|-- var|-- mnt

    每个挂载点(mount)都属于同一棵树。

    VFS 的使命是维护这棵树的统一性。

    2.3 缓存与性能优化

    VFS 提供统一的缓存体系,主要包括:

    • dentry cache:路径解析缓存

    • inode cache:元数据缓存

    • page cache:数据缓存

    这些缓存是 Linux 文件系统性能的核心。

    2.4 可扩展性与可插拔性

    Linux 的文件系统可以动态加载、卸载,支持:

    • 直接内核模块(如 ext4、xfs)

    • FUSE(用户态文件系统)

    • 网络文件系统(NFS、CIFS)

    VFS 的抽象设计保证了系统的可扩展性。


    3. VFS 的关键概念与术语

    下面是 VFS 的核心概念,必须理解清楚才能读懂后续内容。

    3.1 inode(索引节点)

    inode 代表一个文件(或目录、设备文件等)的元数据,包括:

    • 文件类型(普通文件、目录、字符设备、块设备等)

    • 权限(rwx)

    • 所属用户/组

    • 文件大小

    • 时间戳(atime、mtime、ctime)

    • 数据块指针(指向磁盘块)

    • 链接计数(硬链接数)

    • 其他属性(ACL、SELinux 等)

    注意:

    • inode 是文件系统级别的标识

    • 不同文件系统的 inode 号可能重复

    • inode 不包含文件名(文件名存在目录项中)

    3.2 dentry(目录项)

    dentry 是目录项,用于实现:

    • 目录结构

    • 文件名到 inode 的映射

    • 路径解析缓存

    一个 dentry 可能指向一个 inode,也可能是一个“负 dentry”(表示路径不存在)。

    3.3 super_block(超级块)

    super_block 代表一个挂载的文件系统整体信息:

    • 文件系统类型

    • block size

    • 最大文件数

    • 根目录 dentry

    • 相关操作集合(super_operations)

    3.4 file(文件对象)

    file 代表一次打开操作的结果:

    • 由 open 创建

    • 由 close 销毁

    • 保存读写偏移量 f_pos

    • 绑定 file_operations(f_op)

    一个 inode 可以被多个 file 对象引用(即多个进程打开同一文件)。

    3.5 vfsmount(挂载信息)

    vfsmount 代表一个挂载点:

    • 记录挂载点目录(mnt_root)

    • 记录 super_block

    • 形成挂载树(mount tree)


    4. VFS 的核心数据结构

    4.1 主要结构关系图

    process  |  +-- fd table        |        +-- file (open instance)              |              +-- inode (metadata)              |     |              |     +-- super_block (filesystem)              |              +-- dentry (path cache)              |              +-- vfsmount (mount point)

    4.2 数据结构表(关键字段与作用)

    结构
    作用
    关键字段
    典型生命周期
    super_block
    文件系统整体
    s_root, s_op
    挂载/卸载
    inode
    文件元数据
    i_ino, i_mode, i_size, i_nlink
    文件创建/删除
    dentry
    目录项缓存
    d_name, d_inode, d_parent
    路径解析
    file
    打开实例
    f_pos, f_op, f_inode
    open/close
    vfsmount
    挂载点
    mnt_root, mnt_sb
    mount/unmount

    5. VFS 的核心调用流程

    5.1 open() 的流程(核心函数与机制)

    用户态:

    int fd = open("/home/user/a.txt", O_RDONLY);

    open() 流程图

    open()  |sys_open()  |path_lookup()  |dentry cache?  |     \  yes    no  |       |  v       vinode    fs_lookup()  |vfs_open()  |file struct  |return fd

    内核路径:

    1. sys_open / do_sys_open

    2. path_lookupat(路径解析)

    3. nameidata(path 解析上下文)

    4. dentry(路径缓存)

    5. vfs_open(VFS open)

    6. inode

    7. file(创建 file struct)

    8. file->f_op->open(调用文件系统 open)

    5.1.1 路径解析的核心:path_lookup

    path_lookup 是路径解析的核心函数,它会:

    • 逐级解析路径组件

    • 利用 dentry cache 加速

    • 处理符号链接、挂载点、..、. 等

    路径解析是 VFS 性能的重要瓶颈。


    5.2 read() 的流程(含 page cache)

    用户态:

    ssize_t n = read(fd, buf, len);

    内核路径:

    1. sys_read

    2. vfs_read

    3. file->f_op->read_iter(generic_file_read_iter)

    4. page cache 查找

    5. 缓存 miss -> filemap_read -> readpage

    6. 数据拷贝到用户空间

    5.2.1 page cache 的关键:generic_file_read_iter

    generic_file_read_iter 负责:

    • page cache 查找

    • page lock

    • 处理文件末尾

    • 调用底层 readpage 填充 page


    5.3 write() 的流程(含 writeback)

    用户态:

    ssize_t n = write(fd, buf, len);

    内核路径:

    1. sys_write

    2. vfs_write

    3. file->f_op->write_iter(generic_file_write_iter)

    4. 写入 page cache(dirty page)

    5. 后台 writeback 或 sync 写盘

    5.3.1 writeback:什么时候写盘?

    写盘时机取决于:

    • dirty_ratio / dirty_background_ratio

    • 后台 flush 线程

    • sync 或 fsync 强制写盘

    • 文件系统 mount 选项(data=writeback 等)


    6. VFS 与具体文件系统的协作

    6.1 VFS 通过函数指针实现“接口调用”,熟悉内核的朋友应该都知道的

    VFS 的调用链:

    vfs_read -> generic_file_read_iter -> ext4_readpage

    7. VFS 缓存体系:深入剖析

    VFS 缓存体系是 Linux 文件系统性能的核心。下面逐个讲清楚:

    7.1 dentry cache(路径解析缓存)

    dentry cache 的作用是:

    • 缓存目录项(文件名 -> inode 的映射)

    • 加速路径解析

    • 减少目录查找 I/O

    7.1.1 dentry 的引用计数

    每个 dentry 有一个引用计数 d_count。当引用计数为 0 时,dentry 可能被回收。

    7.1.2 负 dentry(negative dentry)

    当路径不存在时,VFS 会创建一个“负 dentry”,用于缓存“文件不存在”的结果,避免重复访问磁盘。


    7.2 inode cache(元数据缓存)

    inode cache 缓存 inode 的元数据,减少读取 inode 的磁盘 I/O。

    inode 的生命周期:

    • 创建(文件创建/打开)

    • 引用计数(i_count)

    • 链接计数(i_nlink)

    • 删除(unlink)

    • 回收(i_count==0 且 i_nlink==0)


    7.3 page cache(数据缓存)

    page cache 是最重要的缓存,它缓存文件内容。几乎所有的文件读写都经过 page cache。

    7.3.1 page cache 的命中逻辑

    • 读:先查 page cache,命中则直接返回

    • 写:写入 page cache,标记 dirty

    • 后台 flush:dirty page 写盘

    7.3.2 readahead(预读)

    当文件被顺序读取时,Linux 会启用 readahead 机制:

    • 预先读取下一段数据到 page cache

    • 提升顺序读性能

    7.3.3 writeback(写回)

    写回策略:

    • 延迟写:提高写性能

    • 但可能造成数据丢失风险(断电/崩溃)

    • 可通过 fsync 强制写回


    8. VFS 的锁与并发控制

    VFS 的并发控制非常复杂,主要依靠以下锁:

    8.1 inode lock(i_mutex / i_rwsem)

    用于保护 inode 元数据和结构一致性。

    典型使用场景:

    • rename

    • truncate

    • unlink

    • setattr

    8.2 dentry lock(d_lock)

    用于保护 dentry 状态,避免路径解析冲突。

    8.3 page lock(page lock)

    保护 page cache 的内容一致性,避免多个线程同时修改同一 page。


    9. 通过典型场景,解释一下 VFS 的行为与性能影响。

    9.1 文件拷贝(cp)的真实性能瓶颈

    cp 的过程:

    open(src)open(dst)read(src)write(dst)

    其中性能瓶颈可能来自:

    • page cache miss(读盘)

    • 写回压力(写盘)

    • 目录缓存 miss(open 很慢)

    • 文件系统碎片

    • 设备 I/O 限制

    9.1.1 性能优化建议

    • 使用 cp --reflink(支持的文件系统,如 btrfs、xfs)

    • 使用 dd if=... of=... bs=... 调整块大小

    • 对大文件使用 O_DIRECT(绕过 page cache,但需要对齐)

    • 使用 rsync --inplace(减少临时文件)


    9.2 rm 删除文件的真实逻辑(为什么 rm 后仍占空间)

    当执行:

    rm /tmp/a

    VFS 做了什么?

    1. path_lookup 找到 dentry

    2. vfs_unlink 删除目录项

    3. inode->i_nlink--

    4. 若 i_nlink==0 且 file 引用计数==0

    5. 释放 inode 和数据块

    但如果文件仍被进程打开(file 仍存在),则:

    • i_nlink 变为 0

    • 但 i_count > 0

    • 文件仍占用磁盘空间

    • 直到最后一个 file 关闭后才释放


    9.3 硬链接与软链接(底层差异)

    9.3.1 硬链接(link)

    硬链接的实现:

    • 新建一个 dentry 指向同一 inode

    • inode->i_nlink++

    硬链接特点:

    • 共享 inode

    • 删除某个链接不影响其他链接

    • 不跨文件系统

    9.3.2 软链接(symlink)

    软链接:

    • dentry 指向一个路径字符串

    • 访问时需要再次解析路径

    软链接特点:

    • 可跨文件系统

    • 可能出现“断链”(目标不存在)


    9.4 mmap(内存映射)的真实机制

    mmap 的作用:

    • 将文件映射到进程虚拟地址空间

    • 读写时直接访问内存页

    • 通过 page fault 触发 page cache 读写

    mmap 的优势:

    • 适合随机读写大文件

    • 减少系统调用开销

    mmap 的风险:

    • 需要注意同步(msync、munmap)

    • 可能导致写回压力集中


    10. VFS 调试与性能优化实践

    缓存体系关系

    [应用程序]     |[系统调用]     |[ VFS ]  |    \  |     \[dentry cache]  [inode cache]  \             /   \           /    \         /     [page cache]          |      [磁盘 I/O]

    10.1 常用工具与命令

    工具
    作用
    场景
    strace
    系统调用跟踪
    查 open/read/write 频率
    perf
    性能分析
    查 CPU / I/O hot path
    ftrace
    内核函数跟踪
    跟踪 vfs_* 调用
    bpftrace
    动态追踪
    追踪路径解析/缓存命中
    iostat
    I/O 统计
    查看磁盘负载
    vmstat
    内存/缓存统计
    观察 page cache
    /proc/sys/vm
    调整缓存策略
    dirty_ratio 等
    dmesg
    文件系统错误
    ext4/xfs 错误日志

    10.2 典型性能问题定位

    10.2.1 读性能差(高 I/O 低 CPU)

    原因:

    • page cache miss

    • 磁盘随机读

    • 文件系统碎片

    • 网络文件系统延迟

    解决:

    • 增加内存(提升 page cache)

    • 调整 readahead

    • 使用更适合的文件系统(xfs、btrfs)

    • 对网络文件系统优化网络参数

    10.2.2 写性能差(大量 dirty page)

    原因:

    • 写回压力集中

    • dirty_ratio 设置过高

    • 大量 small write(导致频繁 flush)

    • fsync 频繁

    解决:

    • 调整 dirty_ratio / dirty_background_ratio

    • 合并写(writev)

    • 减少 fsync 频率

    • 使用 SSD / NVMe


    11. 典型源码解析(硬核部分)

    下面给出一些 VFS 关键函数的逻辑解析(简化版,便于理解)。

    11.1 path_lookup(路径解析)

    核心逻辑:

    1. 分解路径组件

    2. 逐级查找 dentry

    3. 若 dentry cache miss,则调用文件系统 lookup

    4. 处理符号链接、挂载点、权限检查

    路径解析是 VFS 的核心性能瓶颈之一。


    11.2 vfs_read(读取)

    vfs_read 的核心作用:

    • 选择正确的 file_operations

    • 处理不同类型文件(普通文件、管道、字符设备)

    • 走 page cache 或直接调用设备读


    11.3 generic_file_read_iter(通用读取)

    它实现了:

    • page cache 查找

    • 处理 page lock

    • 调用底层 readpage 填充数据

    • 处理 EOF

    这是文件系统读取的通用路径。


    11.4 generic_file_write_iter(通用写入)

    它实现了:

    • 写入 page cache

    • 标记 dirty page

    • 处理 writeback

    • 处理 O_DIRECT 或 mmap 的写入


    12. VFS 的“黑科技”与进阶特性

    12.1 overlayfs(联合挂载)

    overlayfs 允许将多个目录“叠加”在一起:

    upperdir  -> 可写层lowerdir  -> 只读层merged    -> 统一视图

    它的核心价值:(这里在嵌入式系统中常用)

    • 容器镜像写时复制(copy-on-write)

    • 快速构建轻量容器文件系统


    12.2 namespaces(命名空间)

    Linux 支持 mount namespace:

    不同进程组可以看到不同的文件系统树。

    例如容器中:

    • 看到的 / 与宿主机不同

    • mount 操作不会影响宿主机


    13. 常见误区与工程建议

    13.1 “文件系统性能只与磁盘有关”

    错误。性能还与:

    • page cache

    • dentry/inode 缓存

    • 写回策略

    • 文件系统结构

    • CPU 与内存

    密切相关。

    13.2 “关闭缓存就能真实测试”

    错误。关闭缓存会让性能指标失真,因为真实场景下缓存必然存在。正确做法是:

    • 观察缓存命中率

    • 真实场景模拟

    13.3 “fsync 一定要频繁调用”吗?

    答案是不一定啊。因为频繁 fsync 会导致性能下降,但对于数据库等强一致性场景必须,工程上应权衡一致性与性能。


    14. 最后我们总结一下

    1. VFS 是统一抽象层:系统调用 -> VFS -> 具体文件系统

    2. 核心数据结构:super_block、inode、dentry、file、vfsmount

    3. 缓存体系是性能关键:dentry cache、inode cache、page cache

    4. 路径解析是性能瓶颈:dentry cache 命中率决定 open 速度

    5. page cache 是 I/O 性能核心:读写大多数通过 page cache

    6. 锁机制是并发核心:inode lock、dentry lock、page lock

    7. 不同文件系统实现不同:ext4、xfs、nfs 等通过函数指针实现接口

    8. 工程优化要从缓存、I/O、写回策略入手


    如果你觉得本文对你理解 VFS 有帮助,欢迎关注“Linux 炉边会”,我会持续输出:

    • 内核架构解读

    • 性能调优实战

    • 系统工程落地

    • 关键命令与源码分析

    留言告诉我你最想看的主题:

    • VFS 源码逐行拆解?

    • page cache 与 swap 的深度关联?

    • 文件系统性能调优实战?

    • NFS/CIFS 的 VFS 适配机制?

    我会在后面文章中优先输出。

    最新文章

    随机文章