上面六篇文章简要的略过了mount过程如何进入内核,通过VFS层,进入到特定文件系统专有的mount处理函数,读取super block构建struct mount结构体后返回的过程。现在我们得到了一个struct mount挂载实例,接下来要做的就是把这个新的装载实例加入到全局文件系统树中。
sys_mount - > do_mount -> do_new_mount(续)
上面在do_new_mount后沿着vfs_kern_mount -> mount_fs -> xfs_fs_mount -> mount_bdev -> xfs_fs_fill_super的顺序过了一遍,看到了mount的时候是如何利用文件系统提供的mount方法构造出一个Struct mount结构的。现在回到do_new_mount中vfs_kern_mount函数返回的地方,如下:
static int do_new_mount(struct path *path, const char *fstype, int flags,
int mnt_flags, const char *name, void *data)
{
struct file_system_type *type;
struct user_namespace *user_ns = current->nsproxy->mnt_ns->user_ns;
struct vfsmount *mnt;
int err;
//....
//....
// 得到了struct mount结构体,返回其成员vfsmount
mnt = vfs_kern_mount(type, flags, name, data);
// 如果此文件系统还有子类型(多见于FUSE),设置子文件系统类型名
if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&
!mnt->mnt_sb->s_subtype)
mnt = fs_set_subtype(mnt, fstype);
// 就此file_system_type用完了(其mount回调函数完成了构建struct mount的任务)
put_filesystem(type);
if (IS_ERR(mnt))
return PTR_ERR(mnt);
// 准备将得到的mnt结构加入全局文件系统树
// 注意path变量,也就是mountpoint在这里
err = do_add_mount(real_mount(mnt), path, mnt_flags);
if (err)
mntput(mnt);
return err;
}
可以看到vfs_kern_mount返回了一个struct vfsmount结构体的指针,我们上面说了vfsmount是mount结构体中的一个成员(具体参见前面struct mount和struct vfsmount的定义)。
在说do_add_mount前我们先来说一下这里为什么要返回vfsmount这个mount结构体的成员,而不是直接返回mount结构体。对于了解gcc特性的人其实不难理解,linux借助了gcc的一个特性,这个特性可以根据一个结构体的成员计算出这个结构体的首指针。具体可以参见内核中的container_of()函数的实现,或者直接查阅typeof这个gcc提供的关键字。来看do_add_mount的第一个参数real_mount(mnt),它的实现就是借助了container_of(),如下:
static inline struct mount *real_mount(struct vfsmount *mnt)
{
return container_of(mnt, struct mount, mnt);
}
它的意思就是根据struct mount中的mnt成员,计算出mnt这个变量所属的struct mount结构体的地址并返回struct mount的地址。换句话说它就是得到我们上面一直构建出的struct mount结构的首地址。至于为什么要先返回mount的成员,再反过来计算得到mount的地址,这就是另一个辩论不清的问题了。也许是历史原因(以前没有struct mount结构体),或者作者偏好;)