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 系统调用触发。核心路径如下:

  1. 用户态发起 mount 系统调用,进入内核 do_mount 函数。
  2. 路径解析:找到挂载点的 dentry 和 vfsmount。
  3. 调用文件系统特定的 mount 回调:通过 type->mount 函数指针(如 ext4_mount)。
  4. 文件系统驱动填充超级块:驱动调用 sget 或 sget_fc 获取或创建 super_block 实例。
  5. 初始化关键字段:设置 s_op、s_magic、s_flags、s_bdev 等。
  6. 读取根 inode:从磁盘读取根 inode 并创建根 dentry,赋值给 s_root。
  7. 返回根 dentry:VFS 将返回的 dentry 挂接到挂载点。
代码示例:ext4 文件系统的 mount 回调简化流程
// 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 超级块销毁流程

超级块的销毁发生在文件系统卸载或超级块引用计数归零时。核心路径如下:

  1. 用户态发起 umount 系统调用,进入 do_umount 函数。
  2. 检查引用计数:确保没有进程正在使用该文件系统。
  3. 调用 s_op->put_super:文件系统驱动释放私有资源(如日志、元数据缓存)。
  4. 释放根 dentry:调用 dput(sb->s_root) 减少根 dentry 引用计数。
  5. 同步并释放 inode:遍历所有脏 inode 写回磁盘,然后释放 inode 缓存。
  6. 释放超级块自身:调用 kill_block_super 或 kill_anon_super 释放 super_block 内存。
注意: 如果文件系统有未写回的脏数据,put_super 会触发 sync_filesystem 强制同步,确保数据完整性。强制卸载(MNT_FORCE)可能跳过同步,但会导致数据丢失。

3.5 超级块生命周期 SVG 流程图

下图展示了超级块从创建到销毁的完整生命周期,包含关键函数调用路径和状态转换:

超级块生命周期流程图 超级块生命周期流程图 用户态 mount 系统调用 文件系统 mount 回调 (ext4_mount) sget / sget_fc 获取或创建 super_block 填充超级块字段 s_op / s_magic / s_flags / s_bdev 读取根 inode → d_make_root → s_root 挂载完成,返回根 dentry umount 用户态 umount 系统调用 s_op->put_super 释放资源 kill_block_super 释放内存 关键说明 创建阶段由 mount 回调驱动 销毁阶段由 umount 回调驱动

3.6 超级块缓存与复用机制

内核通过 sget 函数实现超级块的缓存与复用,避免同一块设备被多次挂载时重复创建超级块。sget 的核心逻辑如下:

  • 查找:根据 struct file_system_type 和块设备号在全局超级块链表中查找。
  • 匹配:如果找到且引用计数未归零,则直接返回已有超级块(增加引用计数)。
  • 创建:如果未找到,则分配新的 super_block 并插入全局链表。
  • 设置:调用文件系统驱动的 fill_super 回调完成初始化。
代码示例:sget 函数核心逻辑
// 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 能够通过超级块反向找到文件系统驱动,调用正确的操作函数。下图展示了这种关联关系:

超级块与文件系统类型关联图 超级块与 file_system_type 关联关系 file_system_type name: "ext4" mount: ext4_mount s_type super_block s_type → file_system_type s_op → super_operations s_root → 根 dentry 关联意义 VFS 通过 s_type 找到文件系统驱动,通过 s_op 调用具体操作,实现多文件系统支持

3.8 关键设计要点总结

设计要点 说明
引用计数管理 s_count 控制超级块生命周期,每次挂载增加,卸载减少,归零时销毁
缓存复用 sget 机制避免同一设备重复创建超级块,提升挂载性能
操作函数表分离 s_op 将 VFS 通用逻辑与文件系统特定实现解耦
根 dentry 锚点 s_root 是路径查找的起点,所有路径解析都从该 dentry 开始
脏数据同步 销毁前必须同步脏 inode,确保数据一致性
面试高频考点: 超级块与 inode 的关系是什么?超级块存储文件系统全局元数据(块大小、魔数、根 inode 号等),而 inode 存储单个文件的元数据。超级块通过 s_op->alloc_inode 创建 inode,通过 s_root 找到根 inode,形成完整的文件系统视图。