在上一章中,追踪了一个 I/O 请求从用户态 write() 系统调用出发,穿越页缓存、脏页写回,最终抵达块设备层的完整路径。但有一个关键问题被刻意留到了本章:当你调用 open("/home/user/doc.txt", O_RDONLY) 时,内核如何知道这个路径对应磁盘上的哪个位置?为什么同一个 read() 系统调用既能读取 ext4 上的普通文件,也能读取 /proc/cpuinfo 里的内核信息,甚至能读取挂载到 /mnt/usb 的 FAT32 U 盘?
答案就是 VFS(Virtual File System,虚拟文件系统)——Linux 内核中最优雅的抽象层之一。VFS 在用户态系统调用与具体文件系统实现之间插入了一层统一接口,使得”一切皆文件”的 Unix 哲学真正落地。本章将深入剖析 VFS 的四大核心对象、目录项缓存、文件打开的完整路径、ext4 的磁盘布局,以及 procfs/sysfs/tmpfs 等特殊文件系统。
一、VFS 的设计目标
1.1 “一切皆文件”的统一接口
Unix 的核心哲学之一是”一切皆文件”——普通文件、目录、设备、管道、套接字,统统用同一套 open/read/write/close 接口操作。但底层实现千差万别:ext4 的数据在磁盘上按块存储,procfs 的数据由内核函数动态生成,tmpfs 的数据驻留在内存中。VFS 的使命就是在这千差万别之上提供统一抽象。
VFS 的设计目标可以概括为:
- 接口统一:用户态程序无需关心底层文件系统类型,同一套系统调用适用于所有文件系统
- 文件系统无关性:内核子系统(如页缓存、IPC、命名空间)通过 VFS 间接访问文件系统,不直接依赖具体实现
- 可扩展性:新增文件系统类型只需实现 VFS 规定的操作接口,无需修改上层代码
- 跨文件系统操作:支持将不同类型的文件系统挂载到同一棵目录树上,路径解析透明地穿越挂载点
1.2 VFS 的核心抽象
VFS 通过四个核心对象实现上述目标:
| 对象 | 结构体 | 代表什么 | 生命周期 |
|---|---|---|---|
| 超级块 | struct super_block | 一个已挂载的文件系统实例 | 从挂载到卸载 |
| 索引节点 | struct inode | 文件系统中的一个对象(文件/目录/设备) | 从查找到释放 |
| 目录项 | struct dentry | 路径中的一个组成部分,关联名字与 inode | 被 dcache 缓存 |
| 打开文件 | struct file | 进程打开的一个文件实例 | 从 open 到 close |
inode 与 dentry 的区别是理解 VFS 的关键:inode 代表”文件本身”(元数据 + 数据位置),dentry 代表”文件在目录树中的名字”。同一个 inode 可以有多个 dentry(硬链接),一个 dentry 恰好指向一个 inode。
二、四大核心对象详解
2.1 super_block:文件系统的总管
每个已挂载的文件系统在内核中对应一个 struct super_block 实例。它存储了该文件系统的全局元信息,是 VFS 访问具体文件系统的入口。
// include/linux/fs.h(简化)struct super_block { dev_t s_dev; // 设备号 unsigned long s_blocksize; // 块大小(字节) loff_t s_maxbytes; // 文件最大尺寸 struct file_system_type *s_type; // 文件系统类型 const struct super_operations *s_op; // 超级块操作表 struct dentry *s_root; // 根目录项 struct list_head s_inodes; // 该文件系统所有 inode 链表 void *s_fs_info; // 具体文件系统的私有数据 // ... 更多字段};super_operations 定义了 VFS 对超级块的操作接口,由具体文件系统实现:
struct super_operations { struct inode *(*alloc_inode)(struct super_block *sb); // 分配 inode void (*destroy_inode)(struct inode *); // 销毁 inode void (*dirty_inode)(struct inode *, int flags); // 标记脏 inode int (*write_inode)(struct inode *, int wait); // 写回 inode void (*drop_inode)(struct inode *); // 释放 inode 引用 void (*evict_inode)(struct inode *); // 驱逐 inode void (*put_super)(struct super_block *); // 卸载时释放超级块 int (*sync_fs)(struct super_block *, int); // 同步文件系统 int (*statfs)(struct dentry *, struct kstatfs *); // 文件系统统计 int (*remount_fs)(struct super_block *, int *, char *); // 重新挂载 // ...};以 ext4 为例,ext4_sops 实现了上述接口,其中 write_inode 会将内存中的 inode 元数据写回磁盘上的 inode 表。
2.2 inode:文件的身份证
struct inode 是 VFS 中最核心的对象,代表文件系统中的一个实体——无论它是普通文件、目录、符号链接还是设备节点。inode 存储了文件的静态元数据,与文件内容的位置信息。
// include/linux/fs.h(简化)struct inode { umode_t i_mode; // 文件类型与权限 uid_t i_uid; // 所有者 UID gid_t i_gid; // 所有者 GID loff_t i_size; // 文件大小(字节) struct timespec i_atime; // 最后访问时间 struct timespec i_mtime; // 最后修改时间 struct timespec i_ctime; // 最后状态变更时间 const struct inode_operations *i_op; // inode 操作表 const struct file_operations *i_fop; // 文件操作表 struct super_block *i_sb; // 所属超级块 struct address_space *i_data; // 页缓存映射 void *i_private; // 具体文件系统私有数据 // ... 更多字段};inode_operations 定义了与 inode 相关的命名空间操作(创建、查找、链接等):
struct inode_operations { struct dentry *(*lookup)(struct inode *, struct dentry *, unsigned int); // 目录查找 int (*create)(struct inode *, struct dentry *, umode_t, bool); // 创建文件 int (*link)(struct dentry *, struct inode *, struct dentry *); // 硬链接 int (*unlink)(struct inode *, struct dentry *); // 删除目录项 int (*symlink)(struct inode *, struct dentry *, const char *); // 符号链接 int (*mkdir)(struct inode *, struct dentry *, umode_t); // 创建目录 int (*rmdir)(struct inode *, struct dentry *); // 删除目录 int (*rename)(struct inode *, struct dentry *, struct inode *, struct dentry *, unsigned int); // 重命名 int (*getattr)(const struct path *, struct kstat *, ...); // 获取属性 int (*setattr)(struct dentry *, struct iattr *); // 设置属性 // ...};file_operations 定义了已打开文件上的操作(读、写、映射等):
struct file_operations { loff_t (*llseek)(struct file *, loff_t, int); // 定位 ssize_t (*read)(struct file *, char __user *, size_t, loff_t *); // 读 ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *); // 写 int (*mmap)(struct file *, struct vm_area_struct *); // 内存映射 int (*open)(struct inode *, struct file *); // 打开 int (*flush)(struct file *, fl_owner_t); // 刷新 int (*release)(struct inode *, struct file *); // 关闭 int (*fsync)(struct file *, loff_t, loff_t, int); // 同步 // ...};inode_operations 和 file_operations 的分工:前者处理命名空间操作(路径解析、创建/删除/重命名),后者处理文件内容操作(读/写/映射)。这种分离使得目录和文件可以有不同的操作集——目录的 i_op 侧重 lookup,文件的 i_fop 侧重 read/write。
2.3 dentry:路径的骨架
struct dentry 代表目录项(directory entry),是路径中每个组成部分的内核表示。当内核解析路径 /home/user/doc.txt 时,会依次查找或创建四个 dentry:/、home、user、doc.txt。每个 dentry 将一个名字(字符串)与一个 inode 关联起来。
// include/linux/dcache.h(简化)struct dentry { atomic_t d_lockref; // 引用计数 + 自旋锁 unsigned int d_flags; // 标志位 struct qstr d_name; // 名字(哈希字符串) struct inode *d_inode; // 关联的 inode struct dentry *d_parent; // 父目录项 struct list_head d_child; // 父目录的子目录项链表 struct list_head d_subdirs; // 子目录项链表 const struct dentry_operations *d_op; // dentry 操作表 struct super_block *d_sb; // 所属超级块 void *d_fsdata; // 具体文件系统私有数据 // ...};dentry 本身不落盘——它是内核在内存中动态创建的对象,用于加速路径解析。dentry_operations 提供了少量可选的钩子:
struct dentry_operations { int (*d_revalidate)(struct dentry *, unsigned int); // 重新验证(网络文件系统) int (*d_hash)(const struct dentry *, struct qstr *); // 自定义哈希 int (*d_compare)(const struct dentry *, // 名字比较 unsigned int, const char *, const struct qstr *); int (*d_delete)(const struct dentry *); // 删除通知 void (*d_release)(struct dentry *); // 释放 // ...};2.4 file:打开文件的句柄
struct file 代表进程打开的一个文件实例。与 inode 不同,file 是进程上下文相关的——同一个文件被两个进程打开,会产生两个 struct file,各自维护独立的读写偏移量和打开标志。
// include/linux/fs.h(简化)struct file { struct path f_path; // 关联的 dentry + vfsmount const struct file_operations *f_op; // 文件操作表 atomic_long_t f_count; // 引用计数 unsigned int f_flags; // 打开标志(O_RDONLY 等) fmode_t f_mode; // 模式(FMODE_READ/FMODE_WRITE) loff_t f_pos; // 当前读写偏移量 void *private_data; // 私有数据 struct address_space *f_mapping; // 页缓存映射 // ...};文件描述符(fd) 是用户空间对 struct file 的间接引用。每个进程的 task_struct 中有一个 struct files_struct,它维护一个 fdtable——一个 struct file * 指针数组,文件描述符就是该数组的下标。
fd (int) → fdtable.fd_array[fd] → struct file → dentry → inode文件描述符是进程级别的资源。fork() 后子进程继承父进程的 fdtable(COW),但 exec() 默认保留所有已打开的 fd(除非设置了 O_CLOEXEC)。忘记关闭文件描述符是常见的资源泄漏来源。
三、dcache:目录项缓存
3.1 为什么需要 dcache?
路径解析是文件操作中最频繁的工作。每次 open("/home/user/doc.txt") 都需要从根目录开始,逐级查找 home → user → doc.txt。如果每次都从磁盘读取目录项,性能将不可接受。dcache(Directory Entry Cache)就是为解决这一问题而设计的——它缓存最近使用的 dentry 对象,使得路径解析大部分在内存中完成。
dcache 的核心数据结构是一个哈希表,以 {父 dentry 指针, 名字哈希值} 为键,快速定位目标 dentry。同时,所有 dentry 通过 d_parent 和 d_child/d_subdirs 组成一棵目录树,与文件系统的逻辑结构一致。
3.2 dcache 的生命周期管理
dentry 有三种状态:
- 正在使用(in-use):被某个
struct file或子 dentry 引用,d_lockref.count > 0,不能被回收 - 未使用(unused):引用计数为 0,但仍然在缓存中,保留着与 inode 的关联,可以被回收
- 负状态(negative):关联的 inode 已不存在(文件被删除),缓存”此路径不存在”的信息,避免重复磁盘查找
当内存紧张时,内核通过 shrink_dcache_sb() 等函数回收未使用的 dentry。dcache 与 inode 缓存(icache)协同工作——当 dentry 被回收时,它指向的 inode 引用计数减少,如果降为 0,inode 也会被回收。
dcache 是 Linux 文件系统性能的关键优化。实测表明,在典型工作负载下,超过 95% 的路径解析可以在 dcache 中命中,无需访问磁盘。你可以通过 cat /proc/sys/fs/dentry-state 查看 dcache 的状态,其中第一个数字是当前 dentry 总数,第三个是未使用的 dentry 数量。
四、文件打开的完整路径
当用户态程序调用 open("/home/user/doc.txt", O_RDONLY) 时,内核经历了一个复杂的过程。逐步追踪如下:
4.1 系统调用入口
open() 在内核中的入口是 SYSCALL_DEFINE4(openat, ...),最终调用 do_sys_open():
// fs/open.c(简化)long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode){ struct open_flags op; int fd = build_open_flags(flags, mode, &op); // 解析并验证标志 if (fd) return fd;
fd = get_unused_fd_flags(flags); // 分配一个未使用的文件描述符 if (fd >= 0) { struct file *f = do_filp_open(dfd, filename, &op); // 执行路径解析和文件打开 if (IS_ERR(f)) { put_unused_fd(fd); fd = PTR_ERR(f); } else { fsnotify_open(f); fd_install(fd, f); // 将 file 指针安装到 fdtable } } return fd;}4.2 路径解析
do_filp_open() 调用 path_openat(),后者启动路径解析。核心函数是 link_path_walk()(定义在 fs/namei.c),它逐级解析路径分量:
- 从起始目录(绝对路径为根 dentry,相对路径为当前工作目录 dentry)开始
- 取出路径的下一个分量(如
home) - 在 dcache 中查找
{当前 dentry, 分量名}—— 如果命中,直接获得目标 dentry - 如果 dcache 未命中,调用
inode->i_op->lookup()让具体文件系统从磁盘查找 - 文件系统找到对应的 inode 后,创建新的 dentry 并加入 dcache
- 检查挂载点——如果当前 dentry 是一个挂载点,跳转到挂载文件系统的根 dentry
- 重复步骤 2-6,直到路径解析完毕
4.3 文件打开
路径解析完成后,vfs_open() 被调用来完成最后的打开操作:
- 检查权限(
inode_permission()) - 调用
inode->i_fop->open()(如果定义了),让具体文件系统执行初始化 - 创建
struct file实例,设置f_path、f_op、f_mapping等字段 - 将
struct file与文件描述符关联(fd_install())
你可以通过 strace -e openat cat /home/user/doc.txt 观察实际打开过程。更深入地,可以通过 tracefs 或 bpftrace 追踪内核函数调用链:bpftrace -e 'kprobe:do_filp_open { printf("opening: %s\n", str(arg1)); }'
五、read/write 的 VFS 层
5.1 读取路径
当用户调用 read(fd, buf, count) 时,内核的执行路径如下:
// fs/read_write.c(简化流程)SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count) → ksys_read(fd, buf, count) → vfs_read(file, buf, count, &pos) → file->f_op->read(file, buf, count, &pos) // 方式一:直接调用 或 → new_sync_read(file, buf, count, &pos) // 方式二:通用读 → file->f_op->read_iter(&iocb, &iter) // 调用 read_iter现代文件系统(如 ext4)通常实现 read_iter 而非 read,这样可以利用异步 I/O 和直接 I/O 等高级特性。read_iter 内部会通过 address_space(即 inode->i_data)访问页缓存——如果数据已在缓存中,直接拷贝给用户;否则触发缺页,从磁盘读取。
5.2 写入路径
写入路径类似,但涉及脏页标记和写回调度:
// fs/read_write.c(简化流程)SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf, size_t, count) → ksys_write(fd, buf, count) → vfs_write(file, buf, count, &pos) → file->f_op->write_iter(&iocb, &iter) → generic_file_write_iter() → generic_perform_write() // 逐页写入页缓存 → 标记脏页 → 唤醒 writeback 守护进程(如果脏页过多)六、ext4 文件系统结构概览
ext4 是 Linux 上最广泛使用的本地文件系统,也是理解 VFS 与具体文件系统交互的最佳案例。
6.1 磁盘布局
ext4 将磁盘划分为一系列块组(Block Group),每个块组包含:
| 组件 | 作用 |
|---|---|
| 超级块(Superblock) | 文件系统全局元信息(块大小、总块数、inode 数等) |
| 块组描述符表(BGDT) | 每个块组的元信息摘要 |
| 块位图(Block Bitmap) | 标记块组内哪些数据块已使用 |
| inode 位图(Inode Bitmap) | 标记块组内哪些 inode 已使用 |
| inode 表(Inode Table) | 存储该块组所有 inode 的磁盘结构 |
| 数据块(Data Blocks) | 存储文件内容和目录项 |
| 块组0 | 块组1 | 块组2 | ...| SB | BGDT | BB | IB | IT | Data... | SB | BGDT | ... | SB | ...ext4 使用**稀疏超级块(Sparse Superblock)**特性——并非每个块组都有超级块副本,只有块组编号为 0、1 以及 3/5/7 的幂次方的块组才保存超级块副本,节省了大量空间。
6.2 Extent 树
ext4 相比 ext3 的重要改进之一是用 Extent 替代了传统的间接块映射。一个 Extent 描述一段连续的物理块:
// ext4 的 extent 结构(简化)struct ext4_extent { __le32 ee_block; // 逻辑块号(文件内偏移) __le16 ee_len; // 长度(块数) __le16 ee_start_hi; // 物理块号高 16 位 __le32 ee_start_lo; // 物理块号低 32 位};一个 inode 的 extent 以树形结构组织:inode 内嵌一个叶子节点(可容纳 4 个 extent,覆盖最多 4 × 128MB = 512MB 数据);如果文件更大,则升级为索引节点 → 叶子节点的两层结构,最大支持约 4TB 的文件。
6.3 日志机制
ext4 支持**日志(Journal)**来保证崩溃一致性。日志模式有三种:
| 模式 | 说明 | 性能 | 安全性 |
|---|---|---|---|
data=journal | 元数据和文件数据都写入日志 | 最低 | 最高 |
data=ordered(默认) | 元数据写入日志,数据在元数据之前写入磁盘 | 中等 | 高 |
data=writeback | 仅元数据写入日志,数据写入顺序不保证 | 最高 | 最低 |
日志的工作流程简述:事务开始 → 将元数据(或数据)写入日志区 → 提交事务(写入提交块) → 将实际数据写入磁盘对应位置 → 释放日志空间。崩溃恢复时,重放日志中已提交但未完成的事务。
七、特殊文件系统
Linux 内核中有几类不依赖块设备的特殊文件系统,它们的数据完全由内核动态生成,是内核向用户空间暴露内部信息的重要窗口。
7.1 procfs:进程与内核信息
procfs 挂载在 /proc,是最古老也是最重要的特殊文件系统之一。它以文件的形式暴露进程信息和内核参数:
- 进程信息:
/proc/[pid]/目录下的status、maps、fd/、stat等 - 系统信息:
/proc/cpuinfo、/proc/meminfo、/proc/version - 内核参数:
/proc/sys/下的可调参数(通过sysctl修改) - 文件系统信息:
/proc/filesystems、/proc/mounts
procfs 的实现核心是 proc_dir_entry 结构和 proc_ops 操作表。每个 /proc 下的文件都对应一对 show/write 回调函数,读取时调用 show 动态生成内容,写入时调用 write 修改内核参数。
7.2 sysfs:内核对象模型
sysfs 挂载在 /sys,与内核的 kobject/device/driver 模型紧密绑定。它的目录结构反映了内核对象之间的层次关系:
/sys/devices/:所有设备按总线/层级组织/sys/class/:按功能分类的设备(如net/、block/)/sys/bus/:按总线类型组织(如pci/、usb/)/sys/kernel/:内核级参数(如mm/、debug/)/sys/module/:已加载的内核模块信息
sysfs 的每个属性文件通常只包含一个值,遵循”一个文件一个属性”的设计原则,与 procfs 的”一个文件多个信息”风格形成对比。
7.3 tmpfs 与 devtmpfs
tmpfs 是一个基于内存的文件系统,数据完全驻留在物理内存(或交换区)中。它没有磁盘后端,文件系统的大小受 size= 挂载参数限制。常见用途:
- 共享内存(
shm_open()底层使用 tmpfs) /tmp和/run(现代 Linux 发行版默认将它们挂载为 tmpfs)- 临时文件存储
devtmpfs 是 tmpfs 的变体,专门用于 /dev 目录。内核在设备驱动注册/注销时自动在 devtmpfs 中创建/删除设备节点,无需依赖用户态的 udev 守护进程来创建设备文件。
7.4 特殊文件系统对比
| 文件系统 | 挂载点 | 数据来源 | 可写 | 持久化 |
|---|---|---|---|---|
| procfs | /proc | 内核动态生成 | 部分可写(/proc/sys/) | 否 |
| sysfs | /sys | 内核对象属性 | 部分可写 | 否 |
| tmpfs | 可变 | 内存 | 是 | 否(重启丢失) |
| devtmpfs | /dev | 内核自动创建设备节点 | 是 | 否 |
| debugfs | /sys/kernel/debug | 内核调试信息 | 是 | 否 |
八、挂载机制
8.1 vfsmount 与 mount 结构
文件系统挂载是 VFS 与具体文件系统建立关联的过程。内核使用 struct vfsmount 和 struct mount 来描述一个挂载实例:
// fs/mount.h(简化)struct mount { struct list_head mnt_hash; // 全局哈希链表 struct mount *mnt_parent; // 父挂载 struct dentry *mnt_mountpoint; // 挂载点 dentry struct vfsmount mnt; // 嵌入的 vfsmount};
struct vfsmount { struct dentry *mnt_root; // 挂载文件系统的根 dentry struct super_block *mnt_sb; // 挂载文件系统的超级块 int mnt_flags; // 挂载标志};挂载信息通过挂载哈希表组织,以 {父 vfsmount, 挂载点 dentry} 为键。当路径解析遇到一个 dentry 是挂载点时,VFS 会查找哈希表,跳转到挂载文件系统的根 dentry,继续解析。
8.2 挂载命名空间
Linux 2.4.19 引入了挂载命名空间(Mount Namespace),允许不同进程看到不同的文件系统挂载视图。这是容器技术的基础之一:
- 子挂载命名空间继承父命名空间的挂载点
- 子命名空间中的挂载/卸载操作对父命名空间不可见(取决于传播类型)
- 挂载传播类型:
MS_SHARED(共享)、MS_SLAVE(从属)、MS_PRIVATE(私有)、MS_UNBINDABLE(不可绑定)
# 创建新的挂载命名空间unshare --mount /bin/bash
# 在新命名空间中挂载,不影响宿主mount -t tmpfs tmpfs /mnt/test8.3 挂载流程
mount() 系统调用的内核路径:
ksys_mount()→do_mount()解析挂载参数vfs_parse_fs_string()解析文件系统类型和选项fc = fs_context_for_mount()创建文件系统上下文vfs_get_tree(fc)→ 调用文件系统类型的get_tree回调,读取超级块do_new_mount(fc)→ 创建struct mount,关联超级块和挂载点- 将新挂载加入全局哈希表和命名空间的挂载链表
九、动手实践
9.1 观察 VFS 对象
# 查看文件系统类型df -T
# 查看文件的 inode 信息stat /etc/passwd
# 查看目录项缓存状态cat /proc/sys/fs/dentry-state
# 查看系统支持的文件系统类型cat /proc/filesystems
# 查看当前挂载信息cat /proc/mounts# 或mount -l9.2 探索 ext4 文件系统
# 查看 ext4 超级块信息sudo dumpe2fs /dev/sda1 | head -50
# 查看 ext4 块组描述符sudo dumpe2fs /dev/sda1 -bg
# 使用 debugfs 探索 ext4 内部结构sudo debugfs /dev/sda1# 在 debugfs 中:# stat <inode号> 查看 inode 详情# ls -l / 列出根目录# blocks <inode号> 查看文件的块分配# dump <文件> /tmp/out 提取文件内容
# 查看文件的 inode 号和磁盘块ls -lai /etc/passwdsudo hdparm --fibmap /etc/passwd9.3 探索特殊文件系统
# procfs:查看进程信息cat /proc/self/statusls /proc/self/fd/ # 查看当前进程的文件描述符cat /proc/self/maps # 查看内存映射
# procfs:查看内核参数cat /proc/sys/vm/dirty_ratiosudo sysctl vm.dirty_ratio=20 # 修改内核参数
# sysfs:查看设备模型ls /sys/class/net/cat /sys/class/net/eth0/mtuecho 9000 | sudo tee /sys/class/net/eth0/mtu # 修改 MTU
# tmpfs:创建和使用mount -t tmpfs -o size=100M tmpfs /mnt/tmpfsdf -h /mnt/tmpfsdd if=/dev/zero of=/mnt/tmpfs/test bs=1M count=509.4 追踪文件打开路径
# 使用 strace 追踪 open 系统调用strace -e openat cat /etc/hostname
# 使用 bpftrace 追踪内核路径解析sudo bpftrace -e 'kprobe:do_filp_open { printf("path: %s\n", str(arg1)); }'
# 使用 perf 追踪 VFS 操作sudo perf record -e 'vfs:*' -a sleep 5sudo perf script参考资料
内核源码
| 路径 | 内容 |
|---|---|
fs/open.c | do_sys_open()、vfs_open() 实现 |
fs/read_write.c | vfs_read()、vfs_write() 实现 |
fs/namei.c | link_path_walk()、path_lookupat() 路径解析 |
fs/dcache.c | dcache 实现,dentry 分配/查找/回收 |
fs/inode.c | inode 缓存(icache)实现 |
fs/ext4/ | ext4 文件系统完整实现 |
fs/proc/ | procfs 实现 |
fs/sysfs/ | sysfs 实现 |
fs/namespace.c | 挂载命名空间实现 |
include/linux/fs.h | VFS 核心数据结构定义 |
include/linux/dcache.h | dentry 结构定义 |
推荐阅读
- 《深入理解 Linux 内核》 第 12 章——VFS 与文件系统完整论述
- 《Linux 内核设计与实现》 第 13 章——VFS 抽象层与具体文件系统
- Linux 内核文档 — VFS — 官方 VFS 开发者文档
- ext4 Disk Layout — ext4 磁盘布局的权威参考
- Bootlin Elixir — 在线浏览内核文件系统源码
从 VFS 的统一抽象到 ext4 的磁盘布局,从 dcache 的缓存策略到挂载命名空间的隔离——理解了 VFS,你就理解了 Linux 文件系统世界的”宪法”。
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
部分信息可能已经过时






