超级块对象:超级块的作用、关键字段解析、超级块的创建与销毁流程

超级块(Super Block)是文件系统元数据的核心,它描述了整个文件系统的全局信息。在 VFS 层面,超级块对象(struct super_block)是每个已挂载文件系统在内存中的代表;在 EXT4 磁盘布局中,超级块则记录了块组数量、块大小、inode 数量等关键参数。理解超级块是掌握文件系统挂载、卸载与一致性检查的基础。

面试高频点:超级块是文件系统的“身份证”与“户口本”。面试官常问:超级块在磁盘与内存中的区别?EXT4 超级块如何备份?VFS 超级块与 EXT4 超级块如何关联?

2.1 超级块的作用

超级块承担以下核心职责:

  • 全局描述:记录文件系统的类型、大小、状态、挂载计数等全局属性。
  • 定位元数据:提供块组描述符表、inode 表、日志区域等关键结构的起始位置。
  • 一致性保障:通过状态标志(clean/dirty)和校验和,帮助系统在崩溃后判断是否需要 fsck。
  • VFS 抽象:作为 VFS 与具体文件系统(如 EXT4、XFS)之间的桥梁,通过 s_op 函数指针表调用底层实现。
一句话总结:超级块是文件系统的“控制中心”,没有它,内核无法理解磁盘上的数据布局。
超级块在 VFS 与磁盘中的位置 VFS 超级块 (struct super_block) s_dev, s_bdev, s_root, s_op s_fs_info → EXT4 私有数据 s_op 回调 EXT4 超级块 (ext4_super_block) s_blocksize, s_inodes_count s_first_data_block, s_log_groups_per_flex 磁盘布局(块组 0) 超级块 块组描述符 预留 GDT 数据块位图 inode 表 数据块

2.2 关键字段解析

VFS 超级块 struct super_block 包含数十个字段,以下列出面试与开发中最关键的几类:

分类 字段名 类型 说明
设备标识 s_dev dev_t 块设备号,用于关联底层存储设备
块设备 s_bdev struct block_device * 指向块设备描述符,用于 I/O 操作
根目录 s_root struct dentry * 文件系统根目录的 dentry 对象
操作函数表 s_op const struct super_operations * 包含 alloc_inode、destroy_inode、write_super 等回调
文件系统类型 s_type struct file_system_type * 指向注册的文件系统类型(如 ext4_fs_type)
脏标志 s_dirt unsigned long 标记超级块是否被修改,需要写回磁盘
私有数据 s_fs_info void * 指向具体文件系统私有结构(EXT4 中指向 struct ext4_sb_info)
挂载计数 s_count atomic_t 引用计数,防止超级块在未卸载时被释放
注意:EXT4 磁盘超级块 ext4_super_block 与 VFS 超级块是两回事。前者位于磁盘块组 0 的偏移 1024 字节处,后者是内存中的运行时结构。两者通过 ext4_fill_super() 函数建立映射。

2.3 超级块的创建与销毁流程

超级块的创建发生在文件系统挂载时,销毁发生在卸载时。以下以 EXT4 为例,梳理完整流程。

2.3.1 创建流程(挂载时)

  1. 系统调用入口:用户态执行 mount("/dev/sda1", "/mnt", "ext4", 0, NULL),触发 sys_mount() → do_mount()。
  2. 查找文件系统类型:根据 "ext4" 在已注册的 file_system_type 链表中找到 ext4_fs_type。
  3. 分配 VFS 超级块:调用 alloc_super() 分配 struct super_block 内存,初始化 s_list、s_instances、s_op 等字段。
  4. 填充超级块:调用 ext4_fill_super(),该函数执行:
    • 读取磁盘块组 0 的超级块(偏移 1024 字节)到内存。
    • 校验魔数(EXT4_SUPER_MAGIC = 0xEF53)和校验和。
    • 解析 ext4_super_block 字段,填充 ext4_sb_info 私有结构。
    • 设置 s_fs_info 指向 ext4_sb_info。
    • 读取块组描述符表,建立块组索引。
    • 读取根目录 inode,设置 s_root。
  5. 加入全局链表:将超级块加入 super_blocks 全局链表,挂载完成。
EXT4 超级块创建流程(挂载时) sys_mount() alloc_super() ext4_fill_super() 读取磁盘超级块 + 校验 解析 ext4_super_block 填充 ext4_sb_info 设置 s_root 挂载完成

2.3.2 销毁流程(卸载时)

  1. 系统调用入口:用户态执行 umount("/mnt"),触发 sys_umount() → do_umount()。
  2. 同步数据:调用 sync_filesystem() 将脏的 inode、超级块写回磁盘。
  3. 清理 dentry/inode 缓存:调用 shrink_dcache_sb() 和 invalidate_inodes() 释放目录项和 inode 对象。
  4. 调用 kill_sb:VFS 调用 s_op->kill_sb,对于 EXT4 即 ext4_kill_sb():
    • 释放 ext4_sb_info 私有数据。
    • 关闭日志(jbd2)。
    • 释放块组描述符缓存。
  5. 释放超级块:调用 put_super() 减少引用计数,当 s_count 降为 0 时,destroy_super() 释放 struct super_block 内存。
关键代码片段:EXT4 填充超级块的核心入口
// fs/ext4/super.c
static int ext4_fill_super(struct super_block *sb, void *data, int silent)
{
    struct ext4_super_block *es = NULL;
    struct ext4_sb_info *sbi;
    int ret;

    // 1. 分配 ext4_sb_info 私有结构
    sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
    if (!sbi)
        return -ENOMEM;
    sb->s_fs_info = sbi;

    // 2. 读取磁盘超级块(块组 0,偏移 1024 字节)
    es = (struct ext4_super_block *) 
         (__bread(sb->s_bdev, 1, 1024) ? ... : NULL);

    // 3. 校验魔数
    if (le16_to_cpu(es->s_magic) != EXT4_SUPER_MAGIC) {
        ext4_msg(sb, KERN_ERR, "VFS: Can't find ext4 filesystem");
        return -EINVAL;
    }

    // 4. 解析关键字段
    sbi->s_blocksize = 1 << le32_to_cpu(es->s_log_block_size);
    sbi->s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group);
    // ... 更多解析

    // 5. 读取根目录 inode
    sb->s_root = d_make_root(ext4_iget(sb, EXT4_ROOT_INO));
    if (!sb->s_root) {
        ret = -ENOMEM;
        goto failed_mount;
    }

    return 0;

failed_mount:
    // 错误处理
    return ret;
}
面试追问:如果磁盘超级块损坏,EXT4 如何恢复?答:EXT4 在块组 1、3、5…(备份组)中保存超级块副本,e2fsck 或内核挂载时可通过 sb_bread() 读取备份超级块。

2.4 超级块操作函数表

struct super_operations 定义了 VFS 与具体文件系统之间的回调接口,以下是最常用的几个:

回调函数 用途 EXT4 实现
alloc_inode 分配一个新的 inode 对象 ext4_alloc_inode
destroy_inode 销毁 inode 对象 ext4_destroy_inode
write_super 将超级块写回磁盘 ext4_write_super
sync_fs 同步文件系统所有脏数据 ext4_sync_fs
statfs 返回文件系统统计信息(df 命令) ext4_statfs
remount_fs 重新挂载(如 ro → rw) ext4_remount
kill_sb 卸载时清理私有数据 ext4_kill_sb
常见错误:在 write_super 中忘记设置 s_dirt = 0 会导致内核反复尝试写回,造成性能下降。EXT4 已使用 s_dirt 的替代机制(JBD2 日志),但理解这一字段对调试旧版文件系统仍有帮助。

通过以上内容,你应该能够清晰地回答:超级块是什么、它包含哪些关键信息、以及它在挂载/卸载时如何被创建和销毁。这是 VFS 层与 EXT4 层交互的第一个关键环节,也是面试中区分“背题”与“真懂”的分水岭。