Linux根目录的文件系统是如何被挂载的

首先看下mnt_init方法:

// fs/namespace.c
void __init mnt_init(void)
{
        ...
        init_rootfs();
        init_mount_tree();
}

看下其init_rootfs方法:

// init/do_mounts.c
static struct file_system_type rootfs_fs_type = {
        .name           = "rootfs",
        .mount          = rootfs_mount,
        .kill_sb        = kill_litter_super,
};

int __init init_rootfs(void)
{
        int err = register_filesystem(&rootfs_fs_type);
        ...
        return err;
}

该方法主要是用来注册rootfs文件系统。

再看下init_mount_tree方法:

// fs/namespace.c
static void __init init_mount_tree(void)
{
        struct vfsmount *mnt;
        ...
        struct path root;
        struct file_system_type *type;

        type = get_fs_type("rootfs");
        ...
        mnt = vfs_kern_mount(type, 0, "rootfs", NULL);
        ...
        root.mnt = mnt;
        root.dentry = mnt->mnt_root;
        ...
        set_fs_pwd(current->fs, &root);
        set_fs_root(current->fs, &root);
}

该方法首先拿到上面注册的rootfs文件系统,再调用vfs_kern_mount方法挂载该系统,然后将挂载结果mnt赋值给类型为struct path的变量root,同时将root.dentry赋值为mnt->mnt_root,即挂载的rootfs文件系统的根目录。

最后,设置当前进程的当前目录和根目录都为root。

看下vfs_kern_mount方法:

// fs/namespace.c
struct vfsmount *vfs_kern_mount(struct file_system_type *type,
                                int flags, const char *name,
                                void *data)
{
        struct fs_context *fc;
        struct vfsmount *mnt;
        ...
        fc = fs_context_for_mount(type, flags);
        ...
        if (!ret)
                mnt = fc_mount(fc);
        ...
        return mnt;
}
EXPORT_SYMBOL_GPL(vfs_kern_mount);

先看下其fs_context_for_mount方法:

// fs/fs_context.c
struct  fs_context  *fs_context_for_mount(struct file_system_type *fs_type,
                                        unsigned int sb_flags)
{
        return alloc_fs_context(fs_type, NULL, sb_flags, 0,
                                        FS_CONTEXT_FOR_MOUNT);
}
EXPORT_SYMBOL(fs_context_for_mount);

继续看alloc_fs_context方法:

// fs/fs_context.c
static struct fs_context *alloc_fs_context(struct file_system_type *fs_type,
                                      struct dentry *reference,
                                      unsigned int sb_flags,
                                      unsigned int sb_flags_mask,
                                      enum fs_context_purpose purpose)
{
        int (*init_fs_context)(struct fs_context *);
        struct fs_context *fc;
        ...
        fc = kzalloc(sizeof(struct fs_context), GFP_KERNEL);
        ...
        fc->fs_type     = get_filesystem(fs_type);
        ...
        init_fs_context = fc->fs_type->init_fs_context;
        if (!init_fs_context)
                init_fs_context = legacy_init_fs_context;

        ret = init_fs_context(fc);
        ...
        return fc;
        ...
}

由上可知,fc->fs_type指向的rootfs文件系统类型里并没有init_fs_context方法,所以该方法就被赋值为legacy_init_fs_context,之后又调用了该方法。

// fs/fs_context.c
const struct fs_context_operations legacy_fs_context_ops = {
        .free                   = legacy_fs_context_free,
        .dup                    = legacy_fs_context_dup,
        .parse_param            = legacy_parse_param,
        .parse_monolithic       = legacy_parse_monolithic,
        .get_tree               = legacy_get_tree,
        .reconfigure            = legacy_reconfigure,
};

/*
 * Initialise a legacy context for a filesystem that doesn't support
 * fs_context.
 */
static int legacy_init_fs_context(struct fs_context *fc)
{
        fc->fs_private = kzalloc(sizeof(struct legacy_fs_context), GFP_KERNEL);
        ...
        fc->ops = &legacy_fs_context_ops;
        return 0;
}

该方法的主要作用就是将fc->ops字段设置为legacy_fs_context_ops。

继续看下vfs_kern_mount的fc_mount方法:

// fs/namespace.c
struct vfsmount *fc_mount(struct fs_context *fc)
{
        int err = vfs_get_tree(fc);
        if (!err) {
                ...
                return vfs_create_mount(fc);
        }
        ...
}
EXPORT_SYMBOL(fc_mount);

该方法先调用了vfs_get_tree方法,看下:

// fs/super.c
int vfs_get_tree(struct fs_context *fc)
{
        ...
        error = fc->ops->get_tree(fc);
        ...
        return 0;
}
EXPORT_SYMBOL(vfs_get_tree);

由上可知,fc->ops指向的值为legacy_fs_context_ops,所以,fc->ops->get_tree对应的方法为legacy_get_tree。

// fs/fs_context.c
static int legacy_get_tree(struct fs_context *fc)
{
        ...
        struct dentry *root;

        root = fc->fs_type->mount(fc->fs_type, fc->sb_flags,
                                      fc->source, ctx->legacy_data);
        ...
        fc->root = root;
        return 0;
}

该方法又调用了fc->fs_type->mount指向的方法,并将返回值赋值给fc->root,这样,fc->root就指向了该文件系统的跟目录。

由上可知,fc->fs_type->mount指向的方法为rootfs_mount。

// init/do_mounts.c
static struct dentry *rootfs_mount(struct file_system_type *fs_type,
        int flags, const char *dev_name, void *data)
{
        ...
        void *fill = ramfs_fill_super;
        ...
        return mount_nodev(fs_type, flags, data, fill);
}

该方法中,fill变量被设置为ramfs_fill_super方法,然后该方法又调用了mount_nodev方法。

// fs/super.c
struct dentry *mount_nodev(struct file_system_type *fs_type,
        int flags, void *data,
        int (*fill_super)(struct super_block *, void *, int))
{
        int error;
        struct super_block *s = sget(fs_type, NULL, set_anon_super, flags, NULL);
        ...
        error = fill_super(s, data, flags & SB_SILENT ? 1 : 0);
        ...
        return dget(s->s_root);
}
EXPORT_SYMBOL(mount_nodev);

该方法先创建一个struct super_block实例,再调用fill_super指向的方法为该实例赋值,最后返回s->s_root指向的值,即:该文件系统的根目录。

由上可知,fill_super指向的方法为ramfs_fill_super。

// fs/ramfs/inode.c
int ramfs_fill_super(struct super_block *sb, void *data, int silent)
{
        ...
        inode = ramfs_get_inode(sb, NULL, S_IFDIR | fsi->mount_opts.mode, 0);
        sb->s_root = d_make_root(inode);
        ...
        return 0;
}

该方法先调用ramfs_get_inode方法创建并初始化一个inode,再调用d_make_root方法生成一个dentry,并将inode的值赋值给dentry的d_inode字段。

生成的dentry实例最后被赋值给了sb->s_root,这样sb->s_root就指向了该文件系统的根目录。

返回上面的fc_mount方法,通过调用vfs_get_tree方法,fc->root的值就指向了rootfs文件系统的根目录。

再看下fc_mount中的的vfs_create_mount方法:

// fs/namespace.c
struct vfsmount *vfs_create_mount(struct fs_context *fc)
{
        struct mount *mnt;
        ...
        mnt = alloc_vfsmnt(fc->source ?: "none");
        ...
        mnt->mnt.mnt_sb         = fc->root->d_sb;
        mnt->mnt.mnt_root       = dget(fc->root);
        mnt->mnt_mountpoint     = mnt->mnt.mnt_root;
        mnt->mnt_parent         = mnt;
        ...
        return &mnt->mnt;
}
EXPORT_SYMBOL(vfs_create_mount);

由该方法可以看到,mnt->mnt.mnt_root被设置为fc->root,即rootfs文件系统的根目录。

fc_mount方法调用完毕之后退回到vfs_kern_mount方法,vfs_kern_mount方法调用完毕之后退回到init_mount_tree,而在上文也讲到了,init_mount_tree方法里会把root.dentry的值设置为mnt->mnt_root,即rootfs文件系统的根目录,再之后将root的值赋值给当前进程的当前目录和根目录字段。

这样,rootfs文件系统的整个挂载过程就结束了,最终的结果就是,当前进程的根目录就是rootfs文件系统的根目录。

那rootfs文件系统的根目录就是我们想要找的根目录吗?

当然不是,我们要找的根目录应该在硬盘上啊。

那硬盘上的文件系统的根目录是在哪里挂载的呢?硬盘上的文件系统和rootfs文件系统又是什么关系呢?

限于篇幅原因,我们下篇文章再讲

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值