3. VFS通用文件模型:文件系统注册与挂载流程、mount系统调用实现、命名空间管理

本章深入剖析VFS层的核心骨架——通用文件模型。理解文件系统如何注册到内核、如何通过mount系统调用挂载到目录树、以及命名空间如何隔离不同进程的视图,是掌握Linux存储栈的关键。我们将从数据结构、流程步骤和内核代码实现三个维度展开。

3.1 文件系统类型注册机制

每个文件系统在可用之前,必须先向VFS层注册其file_system_type结构体。该结构体是文件系统驱动与VFS之间的契约。

3.1.1 核心数据结构:file_system_type

字段 类型 说明
name const char * 文件系统名称,如"ext4"、"xfs"、"ntfs"
fs_flags int 特性标志,如FS_REQUIRES_DEV、FS_USERNS_MOUNT
mount struct dentry *(*mount)(...) 挂载回调,返回根dentry
kill_sb void (*kill_sb)(struct super_block *) 卸载时销毁超级块
next struct file_system_type * 链表指针,链接所有已注册的文件系统

3.1.2 注册流程

  1. 调用入口:模块初始化时调用register_filesystem()。
  2. 链表插入:将file_system_type实例加入全局链表file_systems。
  3. 命名冲突检查:遍历链表,若name已存在则返回-EBUSY。
  4. 模块引用计数:若为可卸载模块,增加模块引用计数。
关键点:register_filesystem() 通常在文件系统模块的 init 函数中调用。例如 ext4 在 ext4_init_fs() 中注册。卸载时调用 unregister_filesystem() 从链表移除。

3.1.3 注册与挂载关系SVG图

文件系统注册与挂载关系图 文件系统注册与挂载关系 模块加载 register_filesystem() file_systems 链表 mount 系统调用 path_mount() do_new_mount() 查找 fstype → mount 回调 遍历查找 注册:建立类型契约 挂载:实例化超级块

3.2 mount系统调用实现

mount系统调用是用户空间与VFS挂载逻辑的桥梁。其核心路径从SYSCALL_DEFINE5(mount, ...)开始,经过权限检查、路径解析、超级块创建,最终将挂载点与根dentry关联。

3.2.1 调用链与关键函数

函数 所在文件 职责
ksys_mount() fs/namespace.c 用户态入口,复制参数,调用path_mount()
path_mount() fs/namespace.c 解析挂载源与目标路径,权限检查
do_new_mount() fs/namespace.c 分配mount结构,调用文件系统mount回调
vfs_create_mount() fs/namespace.c 创建mount实例并关联超级块
attach_recursive_mnt() fs/namespace.c 将新挂载点插入命名空间树

3.2.2 核心流程步骤

  1. 参数复制与校验:从用户空间复制source、target、filesystemtype、mountflags、data。
  2. 路径解析:通过user_path_at()获取目标路径的path结构(含dentry和vfsmount)。
  3. 权限检查:调用may_mount()检查挂载权限,ns_capable()检查命名空间能力。
  4. 文件系统查找:遍历file_systems链表,匹配filesystemtype名称。
  5. 创建超级块:调用文件系统的mount回调,返回根dentry和超级块。
  6. 挂载点关联:创建mount结构,将根dentry挂接到目标dentry上。
  7. 命名空间插入:调用attach_recursive_mnt()将新挂载点加入当前进程的命名空间树。
性能提示:mount 系统调用在路径解析阶段会触发 path_walk(),对于深层目录树可能产生较大开销。频繁挂载/卸载操作(如容器场景)建议使用 mount --bind 或 pivot_root 减少超级块创建成本。

3.2.3 mount系统调用流程SVG图

mount系统调用完整流程 mount系统调用完整流程 用户态 mount() ksys_mount() path_mount() 权限检查 may_mount() 路径解析 user_path_at() do_new_mount() 查找 file_system_type mount回调 → 超级块 attach_recursive_mnt() 关键数据结构 mount + vfsmount super_block + dentry

3.3 命名空间管理

Linux命名空间(Namespace)是容器技术的核心基石之一。挂载命名空间(Mount Namespace)隔离了进程对挂载点视图的感知,使得每个容器可以拥有独立的文件系统层次结构。

3.3.1 核心数据结构:mnt_namespace

字段 类型 说明
count atomic_t 引用计数,决定命名空间何时销毁
root struct mount * 命名空间的根挂载点
list struct list_head 挂载点链表,遍历所有挂载
user_ns struct user_namespace * 关联的用户命名空间,用于权限检查
seq unsigned int 序列号,用于检测挂载点变更

3.3.2 命名空间操作

  • 创建:clone() 或 unshare() 时指定 CLONE_NEWNS 标志,内核调用 create_mnt_ns() 复制当前命名空间。
  • 切换:switch_mnt_ns() 更新进程的 nsproxy 指针,指向新的命名空间。
  • 传播:挂载事件通过 propagate_mnt() 在共享挂载组(peer group)之间传播。
  • 销毁:当最后一个进程退出或调用 umount 清理所有挂载点时,命名空间被释放。
注意:挂载命名空间的传播类型(MS_SHARED、MS_PRIVATE、MS_SLAVE、MS_UNBINDABLE)决定了挂载事件是否以及如何传播到其他命名空间。默认情况下,新命名空间中的挂载是私有的,不会影响父命名空间。

3.3.3 命名空间隔离与挂载传播SVG图

挂载命名空间隔离与传播 挂载命名空间隔离与传播 初始命名空间 (init_ns) / (root) /home /var /tmp 子命名空间 A / (root) /app /data 子命名空间 B / (root) /opt clone(CLONE_NEWNS) clone(CLONE_NEWNS) 挂载传播类型 • MS_PRIVATE:挂载事件不传播,完全隔离 • MS_SHARED:挂载事件在peer group内双向传播 • MS_SLAVE:接收主组的传播,但不反向传播 • MS_UNBINDABLE:禁止作为绑定挂载的源

3.4 关键数据结构关系总结

VFS通用文件模型的核心数据结构之间通过指针形成网状关系,理解这些关系是掌握挂载流程的基础。

数据结构 角色 关键关联
file_system_type 文件系统类型描述符 全局链表,提供mount回调
super_block 已挂载文件系统的超级块 关联file_system_type、根dentry
mount 挂载实例(内核内部) 包含mnt_mountpoint、mnt_root、mnt_parent
vfsmount 挂载实例(对外接口) 指向mount,包含mnt_sb、mnt_root
dentry 目录项缓存 构成目录树,d_op指向文件系统操作
mnt_namespace 挂载命名空间 包含root挂载点、挂载链表
总结:文件系统注册是“类型定义”,挂载是“实例化”,命名空间是“视图隔离”。三者共同构成了VFS通用文件模型的完整生命周期。理解mount系统调用如何串联file_system_type、super_block、mount和mnt_namespace,是优化文件系统性能的前提。