超级块对象:超级块的作用、关键字段解析、超级块的创建与销毁流程
超级块(Super Block)是文件系统元数据的核心,它描述了整个文件系统的全局信息。在 VFS 层面,超级块对象(struct super_block)是每个已挂载文件系统在内存中的代表;在 EXT4 磁盘布局中,超级块则记录了块组数量、块大小、inode 数量等关键参数。理解超级块是掌握文件系统挂载、卸载与一致性检查的基础。
面试高频点:超级块是文件系统的“身份证”与“户口本”。面试官常问:超级块在磁盘与内存中的区别?EXT4 超级块如何备份?VFS 超级块与 EXT4 超级块如何关联?
2.1 超级块的作用
超级块承担以下核心职责:
- 全局描述:记录文件系统的类型、大小、状态、挂载计数等全局属性。
- 定位元数据:提供块组描述符表、inode 表、日志区域等关键结构的起始位置。
- 一致性保障:通过状态标志(
clean/dirty)和校验和,帮助系统在崩溃后判断是否需要 fsck。 - VFS 抽象:作为 VFS 与具体文件系统(如 EXT4、XFS)之间的桥梁,通过
s_op函数指针表调用底层实现。
一句话总结:超级块是文件系统的“控制中心”,没有它,内核无法理解磁盘上的数据布局。
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 创建流程(挂载时)
- 系统调用入口:用户态执行
mount("/dev/sda1", "/mnt", "ext4", 0, NULL),触发sys_mount()→do_mount()。 - 查找文件系统类型:根据 "ext4" 在已注册的
file_system_type链表中找到ext4_fs_type。 - 分配 VFS 超级块:调用
alloc_super()分配struct super_block内存,初始化s_list、s_instances、s_op等字段。 - 填充超级块:调用
ext4_fill_super(),该函数执行:- 读取磁盘块组 0 的超级块(偏移 1024 字节)到内存。
- 校验魔数(
EXT4_SUPER_MAGIC = 0xEF53)和校验和。 - 解析
ext4_super_block字段,填充ext4_sb_info私有结构。 - 设置
s_fs_info指向ext4_sb_info。 - 读取块组描述符表,建立块组索引。
- 读取根目录 inode,设置
s_root。
- 加入全局链表:将超级块加入
super_blocks全局链表,挂载完成。
2.3.2 销毁流程(卸载时)
- 系统调用入口:用户态执行
umount("/mnt"),触发sys_umount()→do_umount()。 - 同步数据:调用
sync_filesystem()将脏的 inode、超级块写回磁盘。 - 清理 dentry/inode 缓存:调用
shrink_dcache_sb()和invalidate_inodes()释放目录项和 inode 对象。 - 调用 kill_sb:VFS 调用
s_op->kill_sb,对于 EXT4 即ext4_kill_sb():- 释放
ext4_sb_info私有数据。 - 关闭日志(jbd2)。
- 释放块组描述符缓存。
- 释放
- 释放超级块:调用
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 层交互的第一个关键环节,也是面试中区分“背题”与“真懂”的分水岭。