3. 超级块对象 (super_block):结构体定义、关键字段解析、超级块的创建与销毁流程
超级块对象是 VFS 层的核心数据结构之一,用于描述一个已挂载文件系统的全局元信息。每个挂载的文件系统实例在内存中对应一个 super_block 实例,它记录了文件系统的类型、大小、状态、操作函数表以及根目录 dentry 等关键信息。本章将深入解析 super_block 的结构体定义、关键字段语义,并详细阐述其从创建到销毁的完整生命周期。
3.1 super_block 结构体定义
super_block 结构体定义在 include/linux/fs.h 中,是 VFS 层最庞大的结构体之一。以下列出其核心成员,按功能模块分组展示:
| 功能模块 | 字段名 | 类型 | 说明 |
|---|---|---|---|
| 文件系统标识 | s_dev |
dev_t |
包含该文件系统的块设备编号 |
s_magic |
unsigned long |
文件系统魔数,用于识别类型(如 ext4 为 0xEF53) | |
s_id |
char[32] |
文件系统名称字符串(如 "sda1") | |
| 挂载与生命周期 | s_count |
atomic_t |
引用计数,控制超级块存活 |
s_flags |
unsigned long |
挂载标志(如 MS_RDONLY、MS_NOATIME) | |
| 操作函数表 | s_op |
const struct super_operations * |
超级块操作函数指针表(alloc_inode、destroy_inode、sync_fs 等) |
s_d_op |
const struct dentry_operations * |
该文件系统默认的 dentry 操作 | |
s_export_op |
const struct export_operations * |
NFS 导出操作 | |
| 目录树根 | s_root |
struct dentry * |
文件系统根目录的 dentry 对象 |
s_mounts |
struct list_head |
挂载点链表头 | |
s_bdev |
struct block_device * |
关联的块设备结构体 | |
| 脏页与同步 | s_dirty |
struct list_head |
脏 inode 链表 |
s_write_count |
atomic_t |
正在进行的写操作计数 | |
| 时间戳 | s_time_min |
time64_t |
文件系统支持的最小时间戳 |
s_time_max |
time64_t |
文件系统支持的最大时间戳 |
s_op 是超级块对象的行为核心,文件系统驱动必须实现 alloc_inode 和 destroy_inode 两个回调,否则 VFS 无法创建或销毁该文件系统的 inode 对象。
3.2 关键字段深度解析
以下对几个影响 VFS 整体行为的核心字段进行深入分析:
3.2.1 s_op:超级块操作函数表
s_op 指向 struct super_operations,该结构体定义了文件系统驱动必须或可选实现的回调函数。核心回调包括:
- alloc_inode:分配一个该文件系统私有的 inode 结构体(通常包含 VFS inode 作为内嵌成员)。
- destroy_inode:释放由 alloc_inode 分配的内存。
- write_inode:将脏 inode 写回磁盘。
- sync_fs:同步文件系统所有脏数据到磁盘。
- put_super:卸载文件系统时释放超级块自身资源。
- statfs:返回文件系统统计信息(如 df 命令的数据来源)。
3.2.2 s_root:根 dentry
s_root 指向文件系统根目录的 dentry 对象。该 dentry 在超级块创建时由文件系统驱动通过 d_make_root 函数创建,并关联到根 inode。VFS 路径查找的起点就是 s_root。
3.2.3 s_flags:挂载标志
s_flags 是一个位掩码,控制文件系统的挂载行为。常用标志包括:
| 标志 | 值 | 含义 |
|---|---|---|
MS_RDONLY |
1 | 只读挂载,禁止写操作 |
MS_NOSUID |
2 | 忽略 suid/sgid 位 |
MS_NOEXEC |
4 | 禁止执行二进制文件 |
MS_NOATIME |
1024 | 不更新访问时间,提升性能 |
MS_LAZYTIME |
2097152 | 延迟更新 inode 时间戳 |
3.3 超级块创建流程
超级块的创建发生在文件系统挂载过程中,由 mount 系统调用触发。核心路径如下:
- 用户态发起 mount 系统调用,进入内核
do_mount函数。 - 路径解析:找到挂载点的 dentry 和 vfsmount。
- 调用文件系统特定的 mount 回调:通过
type->mount函数指针(如ext4_mount)。 - 文件系统驱动填充超级块:驱动调用
sget或sget_fc获取或创建super_block实例。 - 初始化关键字段:设置
s_op、s_magic、s_flags、s_bdev等。 - 读取根 inode:从磁盘读取根 inode 并创建根 dentry,赋值给
s_root。 - 返回根 dentry:VFS 将返回的 dentry 挂接到挂载点。
// fs/ext4/super.c
static struct dentry *ext4_mount(struct file_system_type *fs_type,
int flags, const char *dev_name,
void *data)
{
struct dentry *root;
// 1. 获取或创建 super_block
root = mount_bdev(fs_type, flags, dev_name, data, ext4_fill_super);
// 2. ext4_fill_super 内部会调用 sget 并填充 s_op、s_root 等
return root;
}
// ext4_fill_super 核心片段
static int ext4_fill_super(struct super_block *sb, void *data, int silent)
{
// 设置超级块操作表
sb->s_op = &ext4_sops;
sb->s_magic = EXT4_SUPER_MAGIC;
// 读取根 inode
inode = ext4_iget(sb, EXT4_ROOT_INO);
sb->s_root = d_make_root(inode);
// ...
}
3.4 超级块销毁流程
超级块的销毁发生在文件系统卸载或超级块引用计数归零时。核心路径如下:
- 用户态发起 umount 系统调用,进入
do_umount函数。 - 检查引用计数:确保没有进程正在使用该文件系统。
- 调用
s_op->put_super:文件系统驱动释放私有资源(如日志、元数据缓存)。 - 释放根 dentry:调用
dput(sb->s_root)减少根 dentry 引用计数。 - 同步并释放 inode:遍历所有脏 inode 写回磁盘,然后释放 inode 缓存。
- 释放超级块自身:调用
kill_block_super或kill_anon_super释放super_block内存。
put_super 会触发 sync_filesystem 强制同步,确保数据完整性。强制卸载(MNT_FORCE)可能跳过同步,但会导致数据丢失。
3.5 超级块生命周期 SVG 流程图
下图展示了超级块从创建到销毁的完整生命周期,包含关键函数调用路径和状态转换:
3.6 超级块缓存与复用机制
内核通过 sget 函数实现超级块的缓存与复用,避免同一块设备被多次挂载时重复创建超级块。sget 的核心逻辑如下:
- 查找:根据
struct file_system_type和块设备号在全局超级块链表中查找。 - 匹配:如果找到且引用计数未归零,则直接返回已有超级块(增加引用计数)。
- 创建:如果未找到,则分配新的
super_block并插入全局链表。 - 设置:调用文件系统驱动的
fill_super回调完成初始化。
// fs/super.c
struct super_block *sget(struct file_system_type *type,
int (*test)(struct super_block *, void *),
int (*set)(struct super_block *, void *),
int flags, void *data)
{
struct super_block *sb = NULL;
// 1. 遍历全局超级块链表,调用 test 回调匹配
list_for_each_entry(sb, &type->fs_supers, s_instances) {
if (test(sb, data)) {
sb->s_count++; // 增加引用计数
return sb; // 复用已有超级块
}
}
// 2. 未匹配,分配新超级块
sb = alloc_super(type, flags);
// 3. 调用 set 回调初始化(通常设置 s_dev 等)
set(sb, data);
// 4. 插入文件系统类型链表
list_add(&sb->s_instances, &type->fs_supers);
return sb;
}
3.7 超级块与文件系统类型的关联
每个 super_block 通过 s_type 字段指向其所属的 file_system_type 结构体。这种关联使得 VFS 能够通过超级块反向找到文件系统驱动,调用正确的操作函数。下图展示了这种关联关系:
3.8 关键设计要点总结
| 设计要点 | 说明 |
|---|---|
| 引用计数管理 | s_count 控制超级块生命周期,每次挂载增加,卸载减少,归零时销毁 |
| 缓存复用 | sget 机制避免同一设备重复创建超级块,提升挂载性能 |
| 操作函数表分离 | s_op 将 VFS 通用逻辑与文件系统特定实现解耦 |
| 根 dentry 锚点 | s_root 是路径查找的起点,所有路径解析都从该 dentry 开始 |
| 脏数据同步 | 销毁前必须同步脏 inode,确保数据一致性 |
s_op->alloc_inode 创建 inode,通过 s_root 找到根 inode,形成完整的文件系统视图。