【Linux文件系统- mount原理分析】

Linux文件系统- mount原理分析


一、数据结构

文件系统建立了一个组织形式来对磁盘中的数据进行管理,如果我们需要对磁盘进行访问的话,首先当前内核系统应该支持该文件系统格式,然后将磁盘挂载到该系统的某个路径下。而挂载的过程是一个复杂的过程,因为挂载之后,就将挂载的目录项dentry与该磁盘文件系统的根目录项进行了关联,我们对该路径的访问就变成了对磁盘的访问(简单理解就是对这个路径下IO操作,底层是由磁盘内部的文件系统来决定了)。同一个路径,是可以挂载多次的,当前的文件系统由最后一次挂载磁盘的文件系统来决定;umount之后,由上一次挂载的磁盘文件系统来决定(也就是顺序的mount和umount)

  • struct mountpoint结构
// fs/mount.h
struct mountpoint {
	struct hlist_node m_hash;    //散列链表节点成员,将mountpoint实例添加到全局散列表mountpoint_hashtable
	struct dentry *m_dentry;     // 指向挂载点 dentry 实例(根文件系统中目录项,不是挂载磁盘中的)
	struct hlist_head m_list;    // 挂载点挂载操作的Mount实例,链表头
	int m_count;                 // 挂载点执行挂载操作的次数
};
  • struct mount 一次挂载所需要涉及的结构数据(fs/mount.h)
  • struct vfsmount 建立mount实例与挂载文件系统之间的关联(include/linux/mount.h)
struct vfsmount {
	struct dentry *mnt_root;	     //指向挂载文件系统根目录项dentry实例
	struct super_block *mnt_sb;	   //指向文件系统超级块实例
	int mnt_flags;                 //内核内部使用的挂载标记
	struct user_namespace *mnt_userns;
} __randomize_layout;

每一个挂载实例对应一个struct vfsmount,包含了于挂载磁盘文件系统的根路径和super_block的关联;而mount是对vfsmount的封装。

二、挂载流程

用户进程通过mount()系统调用挂载具体文件系统,内核mount函数入口为sys_mount(),实现在fs/namespace.c中。其中dev_name定义了块设备路径;di_name定义了挂载点目录;type定义了文件系统类型;flags挂载标志,data定义了一些挂载选项

SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
		char __user *, type, unsigned long, flags, void __user *, data)
{
	int ret;
	char *kernel_type;
	char *kernel_dev;
	void *options;
  /* 拷贝文件系统类型名到内核空间 */
	kernel_type = copy_mount_string(type);
	ret = PTR_ERR(kernel_type);
	if (IS_ERR(kernel_type))
		goto out_type;
  /* 拷贝块设备路径名到内核空间 */
	kernel_dev = copy_mount_string(dev_name);
	ret = PTR_ERR(kernel_dev);
	if (IS_ERR(kernel_dev))
		goto out_dev;
  /* 拷贝挂载选项到内核空间 */
	options = copy_mount_options(data);
	ret = PTR_ERR(options);
	if (IS_ERR(options))
		goto out_data;
  /* 挂载委托do_mount,最重要的接口实现 */
	ret = do_mount(kernel_dev, dir_name, kernel_type, flags, options);

	kfree(options);
out_data:
	kfree(kernel_dev);
out_dev:
	kfree(kernel_type);
out_type:
	return ret;
}

内核系统调用流程

SYSCALL_DEFINE5(mount...)    //namespace.c
    do_mount()
        user_path()
        do_remount()
        do_loopback()
        do_change_type()
        do_move_mount()
        do_new_mount()
            struct file_system_type *type
            struct vfs_mount *mnt
            mnt = vfs_kern_mount()
                struct mount * mnt
                mnt = alloc_vfsmnt()
                root = mount_fs()
                    struct super_block *sb
                    root = type->mount()        //回调file_system_type的mount方法
                    sb = root ->d_sb
                    security_sb_kern_mount()
                    up_write()
                init mnt
                list_add_tail(&mnt->mnt_instance, &root->d_sb->s_mounts)
            do_add_mount(real_mount(mnt),path,mnt_flags)
ext4_mnt()        //ext4
    mount_bdev(...ext4_fill_super)
        struct block_device *bdev
        struct super_block *s
        bdev = blkdev_get_by_path()
        s = sget(...test_bdev_super,set_bdev_super...)        //find or create a superblock
            alloc_super()
                kzalloc()
                init_waitqueue_head()
                s->s_bdi = &noop_backing_dev_info
                ...other initialization of s
            set()                            //set_bdev_super()
                s->s_bdev = data;
                s->s_dev = s->s_bdev->bd_dev;
                s->s_bdi = &bdev_get_queue(s->s_bdev)->backing_dev_info;
                    bdev->bd_disk->queue;
            list_add_tail(...&super_blocks)
            hlist_add_head()
            get_filesystem()
                __module_get()
        sb_set_blocksize()
        fill_super()                        //ext4_fill_super()
            struct ext4_sb_info *sbi
            sbi = kzalloc()
            ... init sbi...
            ext4_msg()
            setup_timer()
            sb->s_op = &ext4_sops
            sb->s_export_op = &ext4_export_ops
            sb->s_xattr = ext4_xattr_handlers
            sb->s_root = d_make_root()
            ext4_setup_super()
            ext4_ext_init()
            ext4_mb_init()
            sbi->s_kobj.kset = ext4_kset
            init_completion()
            kobject_init_and_add(&sbi->s_kobj, &ext4_ktype...)
        s->s_flags|=MS_ACTIVE
        bdev->bd_super = s
        dget()

## 3 总结

对于内核的Mount流程,内核定义了一个Mountpoint结构体表示跟文件系统中的一个挂载点,挂载点对应跟文件系统中的一个dentry实例,用户通过mount系统调用实现文件系统的挂载,其主要流程为:

- 执行内核的挂载函数vfs_kern_mount:该函数主要是创建文件系统超级块super_block、根目录项dentry和inode结构体实例,并创建表示本次挂载操作的mount结构体实例,mount实例添加到超级块实例s_mounts成员链表中,并与挂载文件系统根目录项dentry建立关联
![在这里插入图片描述](https://img-blog.csdnimg.cn/8f4a88062b944f7bb038d62c6a22c945.png#pic_center)


- 关联挂载点do_add_mount:创建挂载点mountpoint结构体实例,并添加到全局散列表,mountpoint实例关联到挂载点dentry实例(跟文件系统中目录项),并将挂载mount实例添加到Mountpoint实例链表和全局散列表中,建立mount实例与挂载断点dentry之间的关联,一个挂载点可以有多个挂载,因此Mountpoint实例包含一个挂载mount实例的链表

![在这里插入图片描述](https://img-blog.csdnimg.cn/6e7019d5d4bd44d588d33714158057d9.png#pic_center)


执行完这两步,通过mount实例建立了挂载点dentry实例和挂载文件系统根目录项dentry实例之间的联系。

![在这里插入图片描述](https://img-blog.csdnimg.cn/09b603f522ee40e38a7309771e5b8487.png#pic_center)

当内核打开文件搜索路径到达挂载点时(挂载点dentry实例设置DCACHE_MOUNTED标记位),将调用函数lookup_mnt(path),在mount实例全局散列表中查找第一个关联到挂载点dentry实例的mount实例,搜索路径随后进入mount实例关联的挂载文件系统根目录项。

## 4 参考文档

[https://blog.51cto.com/u_15127540/3543420](https://blog.51cto.com/u_15127540/3543420)

linux内核解析

参考文档:文件系统mount过程

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

自动驾驶小哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值