2. Linux 文件系统基础:VFS(虚拟文件系统)架构详解
在 Linux 内核中,VFS(Virtual File System,虚拟文件系统) 是文件系统子系统的核心抽象层。它位于用户空间与具体文件系统实现(如 ext4、SquashFS、NFS)之间,为应用程序提供统一的文件操作接口(open、read、write、close 等),而无需关心底层存储介质或文件系统的具体布局。
2.1 VFS 的四大核心对象
VFS 基于面向对象的设计思想,定义了四种关键的数据结构,它们共同构成了文件系统的运行时视图:
| 对象 | 数据结构 | 生命周期 | 核心作用 |
|---|---|---|---|
| 超级块 (Super Block) | struct super_block |
文件系统挂载时创建,卸载时销毁 | 存储整个文件系统的全局元数据(块大小、inode 总数、挂载选项等) |
| 索引节点 (inode) | struct inode |
文件被访问时创建,引用计数归零后释放 | 存储文件的元数据(权限、大小、时间戳、数据块指针),不包含文件名 |
| 目录项 (dentry) | struct dentry |
路径解析时创建,由 dentry cache 管理 | 将文件名与对应的 inode 关联起来,构成目录树结构 |
| 文件对象 (file) | struct file |
进程调用 open() 时创建,close() 时销毁 | 代表一个已打开的文件实例,记录读写位置、访问模式、文件描述符标志 |
2.2 四大对象的关系与协作
下图展示了 VFS 四大核心对象之间的协作关系,以及它们如何共同支撑一次典型的文件访问流程:
2.3 超级块 (Super Block) 详解
超级块是文件系统的“控制块”,每个挂载的文件系统在内存中对应一个 struct super_block 实例。它包含了文件系统的全局配置和状态信息。
// 关键字段(简化版)
struct super_block {
dev_t s_dev; // 块设备标识
unsigned long s_blocksize; // 块大小(字节)
unsigned char s_blocksize_bits; // 块大小的位数
loff_t s_maxbytes; // 文件最大大小
struct file_system_type *s_type; // 文件系统类型(ext4、squashfs 等)
struct super_operations *s_op; // 超级块操作函数集
struct dentry *s_root; // 根目录的 dentry
struct list_head s_inodes; // 所有 inode 的链表
struct list_head s_dentry_lru; // dentry LRU 链表
// ... 其他字段
};
s_op 是一个指向 struct super_operations 的指针,该结构体定义了文件系统必须实现的回调函数,如 alloc_inode()、destroy_inode()、write_super() 等。这是 VFS 实现多态的关键机制。
2.4 索引节点 (inode) 详解
inode 是文件系统中最核心的数据结构之一,它存储了文件的所有元数据,唯独不包含文件名。每个文件(包括目录、设备文件、符号链接等)都有一个唯一的 inode 编号。
struct inode {
umode_t i_mode; // 文件类型与权限
uid_t i_uid; // 所有者用户 ID
gid_t i_gid; // 所有者组 ID
kdev_t i_rdev; // 设备文件的设备号
loff_t i_size; // 文件大小(字节)
struct timespec64 i_atime; // 最后访问时间
struct timespec64 i_mtime; // 最后修改时间
struct timespec64 i_ctime; // 最后状态改变时间
unsigned long i_blocks; // 文件占用的块数(512 字节为单位)
struct inode_operations *i_op; // inode 操作函数集
struct super_block *i_sb; // 所属超级块
struct address_space *i_mapping; // 地址空间(页缓存)
// ... 数据块指针(ext4 使用 ext4_inode_info 扩展)
};
i_op 字段指向 struct inode_operations,该结构体定义了创建文件、创建目录、查找、链接、删除等操作。不同文件系统通过实现不同的 i_op 来提供差异化的语义。
2.5 目录项 (dentry) 详解
dentry 是 VFS 中用于路径解析的核心数据结构。它将用户看到的文件名(如 /home/user/file.txt)与内核中的 inode 关联起来。dentry 不直接对应磁盘上的数据结构,而是内存中的缓存对象。
struct dentry {
struct qstr d_name; // 文件名(快速字符串)
struct inode *d_inode; // 指向对应的 inode
struct dentry *d_parent; // 父目录的 dentry
struct list_head d_subdirs; // 子目录项链表
struct list_head d_child; // 兄弟目录项链表
struct dentry_operations *d_op; // dentry 操作函数集
unsigned int d_flags; // 标志位(DCACHE_*)
// ... 哈希表、LRU 链表等
};
| dentry 状态 | 说明 | 典型场景 |
|---|---|---|
| DCACHE_DISCONNECTED | dentry 未连接到全局目录树 | 文件系统挂载过程中的临时状态 |
| DCACHE_REFERENCED | dentry 最近被访问过 | LRU 回收时优先保留 |
| DCACHE_LRU_LIST | dentry 在 LRU 链表中 | 内存压力下的回收候选 |
| DCACHE_DENTRY_KILLED | dentry 已被标记为死亡 | 等待最终释放 |
2.6 文件对象 (file) 详解
文件对象代表一个已打开的文件实例。它与进程的文件描述符一一对应,记录了进程对文件的操作状态。同一个文件可以被多个进程同时打开,每个进程拥有独立的 file 对象。
struct file {
struct path f_path; // 包含 dentry 和 mount 信息
struct inode *f_inode; // 指向文件对应的 inode
const struct file_operations *f_op; // 文件操作函数集
loff_t f_pos; // 当前读写位置(偏移量)
unsigned int f_flags; // 打开标志(O_RDONLY, O_WRONLY 等)
fmode_t f_mode; // 访问模式(读/写/执行)
struct file_ra_state f_ra; // 预读状态
// ... 私有数据、引用计数等
};
f_op 指向 struct file_operations,这是 VFS 中最重要的操作函数集之一,包含了 read()、write()、mmap()、ioctl() 等接口。每个文件系统或设备驱动都可以提供自己的 file_operations 实现。
2.7 四大对象的生命周期与协作流程
下面通过一个典型的文件打开和读取流程,展示四大对象如何协同工作:
- 挂载阶段:内核读取磁盘上的文件系统元数据,创建
super_block对象,并初始化根 dentry (s_root)。 - 路径解析:用户调用
open("/etc/passwd", O_RDONLY),内核从根 dentry 开始,逐级查找路径中的每个分量,在 dcache 中查找或创建新的 dentry。 - inode 加载:当找到目标文件的 dentry 后,如果
d_inode为空,则调用具体文件系统的lookup()方法,从磁盘读取 inode 信息并填充到内存 inode 中。 - file 对象创建:内核分配一个新的
file对象,设置f_path.dentry指向目标 dentry,f_inode指向对应的 inode,f_op从 inode 的默认操作中获取。 - 文件描述符绑定:将 file 对象指针存入进程的文件描述符表,返回文件描述符编号给用户空间。
- 读写操作:用户调用
read(fd, buf, count),内核通过 fd 找到 file 对象,调用f_op->read(),最终通过 inode 的i_mapping访问页缓存或磁盘数据。 - 关闭操作:用户调用
close(fd),内核释放 file 对象,递减 inode 和 dentry 的引用计数。当引用计数归零时,inode 和 dentry 可能被回收。
2.8 操作函数集:VFS 多态的实现机制
VFS 通过四个操作函数集实现多态,每个具体文件系统只需实现这些函数指针:
| 操作函数集 | 所属对象 | 关键函数 | 作用 |
|---|---|---|---|
super_operations |
super_block | alloc_inode, destroy_inode, write_super, sync_fs |
管理 inode 生命周期、同步元数据 |
inode_operations |
inode | lookup, create, mkdir, unlink, symlink |
目录和文件创建、删除、链接等操作 |
dentry_operations |
dentry | d_hash, d_compare, d_delete, d_release |
dentry 哈希、比较、释放等操作 |
file_operations |
file | read, write, mmap, ioctl, open, release |
文件读写、内存映射、设备控制等 |
register_filesystem() 注册到内核。VFS 会自动将我们的操作函数集挂接到对应的对象上。
2.9 本章小结
VFS 是 Linux 文件系统架构的基石,它通过四个核心对象(超级块、inode、dentry、file)和四个操作函数集,实现了对多种文件系统的统一抽象。理解这些对象的关系与生命周期,是后续学习 ext4 和 SquashFS 定制的基础。
| 对象 | 类比 | 关键字段 | 操作函数集 |
|---|---|---|---|
| super_block | 文件系统的“身份证” | s_blocksize, s_root, s_op | super_operations |
| inode | 文件的“户口本” | i_mode, i_size, i_op, i_sb | inode_operations |
| dentry | 文件名的“门牌号” | d_name, d_inode, d_parent | dentry_operations |
| file | 文件的“借阅记录” | f_pos, f_flags, f_op, f_inode | file_operations |