linux mount过程

接下来我们以ext4 文件系统mount过程为例,讲解下文件系统的
几种数据结构之间的关联。

如果linux版本有支持ext4 fs,那么在linux初始化时会调用static int __init ext4_init_fs(void),
这个函数会通过register_filesystem(&ext4_fs_type)向系统注册ext4文件系统到全局file_systems结构中。
注册之后os就可以识别此文件系统,当要使用ext4时,通过mount函数加载ext4 super block,inode信息,

之后就可以进行ext4读写了。


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;
	struct filename *kernel_dir;
	char *kernel_dev;
	unsigned long data_page;
	//复制文件系统类型名到内核
	ret = copy_mount_string(type, &kernel_type);
	if (ret < 0)
		goto out_type;
	//得到挂载点路径名
	kernel_dir = getname(dir_name);
	if (IS_ERR(kernel_dir)) {
		ret = PTR_ERR(kernel_dir);
		goto out_dir;
	}
	//获取文件系统所在设备名,如/dev/sda1
	ret = copy_mount_string(dev_name, &kernel_dev);
	if (ret < 0)
		goto out_dev;
	//获取挂载options信息
	ret = copy_mount_options(data, &data_page);
	if (ret < 0)
		goto out_data;
	//mount主体函数
	ret = do_mount(kernel_dev, kernel_dir->name, kernel_type, flags,
		(void *) data_page);

	free_page(data_page);
out_data:
	kfree(kernel_dev);
out_dev:
	putname(kernel_dir);
out_dir:
	kfree(kernel_type);
out_type:
	return ret;
}

//先检查挂载参数,之后调用不同的mount函数
long do_mount(const char *dev_name, const char *dir_name,
		const char *type_page, unsigned long flags, void *data_page)
{
	struct path path;
	int retval = 0;
	int mnt_flags = 0;
	printk(KERN_ERR "dev_name:%s dir_name:%s \n",dev_name,dir_name);
	/* Discard magic */
	if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
		flags &= ~MS_MGC_MSK;

	/* Basic sanity checks */

	if (!dir_name || !*dir_name || !memchr(dir_name, 0, PAGE_SIZE))
		return -EINVAL;

	if (data_page)
		((char *)data_page)[PAGE_SIZE - 1] = 0;

	/* ... and get the mountpoint */
	//解析dir_name获取挂载路径
	retval = kern_path(dir_name, LOOKUP_FOLLOW, &path);
	if (retval)
		return retval;
	//挂载安全性检测
	retval = security_sb_mount(dev_name, &path,
				   type_page, flags, data_page);
	if (!retval && !may_mount())
		retval = -EPERM;
	if (retval)
		goto dput_out;

	/* Default to relatime unless overriden */
	if (!(flags & MS_NOATIME))
		mnt_flags |= MNT_RELATIME;

	/* Separate the per-mountpoint flags */
	if (flags & MS_NOSUID)
		mnt_flags |= MNT_NOSUID;
	if (flags & MS_NODEV)
		mnt_flags |= MNT_NODEV;
	if (flags & MS_NOEXEC)
		mnt_flags |= MNT_NOEXEC;
	if (flags & MS_NOATIME)
		mnt_flags |= MNT_NOATIME;
	if (flags & MS_NODIRATIME)
		mnt_flags |= MNT_NODIRATIME;
	if (flags & MS_STRICTATIME)
		mnt_flags &= ~(MNT_RELATIME | MNT_NOATIME);
	if (flags & MS_RDONLY)
		mnt_flags |= MNT_READONLY;

	/* The default atime for remount is preservation */
	if ((flags & MS_REMOUNT) &&
	    ((flags & (MS_NOATIME | MS_NODIRATIME | MS_RELATIME |
		       MS_STRICTATIME)) == 0)) {
		mnt_flags &= ~MNT_ATIME_MASK;
		mnt_flags |= path.mnt->mnt_flags & MNT_ATIME_MASK;
	}

	flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE | MS_BORN |
		   MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT |
		   MS_STRICTATIME);

	if (flags & MS_REMOUNT)
		retval = do_remount(&path, flags & ~MS_REMOUNT, mnt_flags,
				    data_page);
	else if (flags & MS_BIND)
		retval = do_loopback(&path, dev_name, flags & MS_REC);
	else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
		retval = do_change_type(&path, flags);
	else if (flags & MS_MOVE)
		retval = do_move_mount(&path, dev_name);
	else
		retval = do_new_mount(&path, type_page, flags, mnt_flags,
				      dev_name, data_page);
dput_out:
	path_put(&path);
	return retval;
}

/*
对于一个新的文件系统初次挂载会调用do_new_mount,这个函数会先给这个
文件系统创建一个struct mount结构,调用文件系统特有的mount函数,
最后将struct 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;

	if (!fstype)
		return -EINVAL;
	//通过name获取文件系统类型
	type = get_fs_type(fstype);
	if (!type)
		return -ENODEV;
	printk(KERN_ERR "fs type:%s\n",type->name);
	
	if (user_ns != &init_user_ns) {
		if (!(type->fs_flags & FS_USERNS_MOUNT)) {
			put_filesystem(type);
			return -EPERM;
		}
		/* Only in special cases allow devices from mounts
		 * created outside the initial user namespace.
		 */
		if (!(type->fs_flags & FS_USERNS_DEV_MOUNT)) {
			flags |= MS_NODEV;
			mnt_flags |= MNT_NODEV | MNT_LOCK_NODEV;
		}
	}
	//获取struct mount结构,调用特定文件系统mount函数,主要填充super block数据
	mnt = vfs_kern_mount(type, flags, name, data);
	//有子文件系统
	if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&
	    !mnt->mnt_sb->s_subtype)
		mnt = fs_set_subtype(mnt, fstype);

	put_filesystem(type);
	if (IS_ERR(mnt))
		return PTR_ERR(mnt);
	//将mount加入到全局文件树中
	err = do_add_mount(real_mount(mnt), path, mnt_flags);
	if (err)
		mntput(mnt);
	return err;
}

struct vfsmount *
vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
{
	struct mount *mnt;
	struct dentry *root;

	if (!type)
		return ERR_PTR(-ENODEV);
	//分配并初始化struct mount 结构
	mnt = alloc_vfsmnt(name);
	if (!mnt)
		return ERR_PTR(-ENOMEM);

	if (flags & MS_KERNMOUNT)
		mnt->mnt.mnt_flags = MNT_INTERNAL;
	//调用具体文件系统的mount函数
	root = mount_fs(type, flags, name, data);
	if (IS_ERR(root)) {
		free_vfsmnt(mnt);
		return ERR_CAST(root);
	}
	//初始化mnt变量,并将mnt加入超级块s_mounts链表中
	mnt->mnt.mnt_root = root;
	mnt->mnt.mnt_sb = root->d_sb;
	mnt->mnt_mountpoint = mnt->mnt.mnt_root;
	mnt->mnt_parent = mnt;
	br_write_lock(&vfsmount_lock);
	list_add_tail(&mnt->mnt_instance, &root->d_sb->s_mounts);
	br_write_unlock(&vfsmount_lock);
	return &mnt->mnt;
}

struct dentry *
mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
{
	struct dentry *root;
	struct super_block *sb;
	char *secdata = NULL;
	int error = -ENOMEM;

	if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) {
		secdata = alloc_secdata();
		if (!secdata)
			goto out;

		error = security_sb_copy_data(data, secdata);
		if (error)
			goto out_free_secdata;
	}
	//具体文件系统的mount函数,比如ext4,该函数就是系统初始化时注册的ext4_fs_type 里面的mount
	root = type->mount(type, flags, name, data);//返回mount后的denty
	if (IS_ERR(root)) {
		error = PTR_ERR(root);
		goto out_free_secdata;
	}
	sb = root->d_sb;
	BUG_ON(!sb);
	WARN_ON(!sb->s_bdi);
	WARN_ON(sb->s_bdi == &default_backing_dev_info);
	sb->s_flags |= MS_BORN;

	error = security_sb_kern_mount(sb, flags, secdata);
	if (error)
		goto out_sb;

	/*
	 * filesystems should never set s_maxbytes larger than MAX_LFS_FILESIZE
	 * but s_maxbytes was an unsigned long long for many releases. Throw
	 * this warning for a little while to try and catch filesystems that
	 * violate this rule.
	 */
	WARN((sb->s_maxbytes < 0), "%s set sb->s_maxbytes to "
		"negative value (%lld)\n", type->name, sb->s_maxbytes);

	up_write(&sb->s_umount);
	free_secdata(secdata);
	return root;
out_sb:
	dput(root);
	deactivate_locked_super(sb);
out_free_secdata:
	free_secdata(secdata);
out:
	return ERR_PTR(error);
}

//newmnt: 新创建的挂载实例 path:挂载路径
static int do_add_mount(struct mount *newmnt, struct path *path, int mnt_flags)
{
	struct mountpoint *mp;
	struct mount *parent;
	int err;

	mnt_flags &= ~(MNT_SHARED | MNT_WRITE_HOLD | MNT_INTERNAL);
    //这里不是简单的加锁,如果path上挂载了很多文件系统,那么这里就是要找出最新一次挂载到其上的文件系统的根路径,这才//是我们这个文件系统要挂载到的mountpoint
	mp = lock_mount(path);
	if (IS_ERR(mp))
		return PTR_ERR(mp);

	parent = real_mount(path->mnt);//得到挂载点所属的挂载结构
	err = -EINVAL;
	if (unlikely(!check_mnt(parent))) {
		/* that's acceptable only for automounts done in private ns */
		if (!(mnt_flags & MNT_SHRINKABLE))
			goto unlock;
		/* ... and for those we'd better have mountpoint still alive */
		if (!parent->mnt_ns)
			goto unlock;
	}

	/* Refuse the same filesystem on the same mount point */
	err = -EBUSY;
	//禁止同一个文件系统挂在到同一个挂载点
	if (path->mnt->mnt_sb == newmnt->mnt.mnt_sb &&
	    path->mnt->mnt_root == path->dentry)
		goto unlock;

	err = -EINVAL;
	if (S_ISLNK(newmnt->mnt.mnt_root->d_inode->i_mode))
		goto unlock;

	newmnt->mnt.mnt_flags = mnt_flags;
	//把newmnt加入到全局文件系统树中
	err = graft_tree(newmnt, parent, mp);  

unlock:
	unlock_mount(mp);
	return err;
}

static struct mountpoint *lock_mount(struct path *path)
{
	struct vfsmount *mnt;
	struct dentry *dentry = path->dentry;
retry:
	mutex_lock(&dentry->d_inode->i_mutex);
	if (unlikely(cant_mount(dentry))) {
		mutex_unlock(&dentry->d_inode->i_mutex);
		return ERR_PTR(-ENOENT);
	}
	namespace_lock();
	mnt = lookup_mnt(path);
	if (likely(!mnt)) {//这里表示dentry上未挂载文件系统,创建一个新的mountpoint 返回
		struct mountpoint *mp = new_mountpoint(dentry);
		if (IS_ERR(mp)) {
			namespace_unlock();
			mutex_unlock(&dentry->d_inode->i_mutex);
			return mp;
		}
		return mp;
	}
	namespace_unlock();
	mutex_unlock(&path->dentry->d_inode->i_mutex);
	path_put(path);
	 // 如果lookup_mnt没有返回NULL,则说明它找到了挂载在/mnt上的子文件系统,下面的逻辑是: 
	 // 把子文件系统的mount结构赋值给path->mnt	
	 path->mnt = mnt;
	//如果此dentry之前挂载了文件系统,则新的dentry将为子文件系统mnt的挂载点
	dentry = path->dentry = dget(mnt->mnt_root);
	// 返回到lookup_mnt函数,用新的path变量继续查找是否还有后续的子文件系统
	//这样组成的list结构:p->C1->C2->C3,从全局来看后挂载的会覆盖之前挂载的文件系统
	goto retry;
}

//参数为挂载点所属的挂载实例跟目录项,dir为移动方向
/*
路径名查找时都会调用到这个函数,它的作用就是根据一个父<mount, dentry>
二元组找到挂载在其下面的子文件系统的mount实例,如果没找到就返回NULL
*/
struct mount *__lookup_mnt(struct vfsmount *mnt, struct dentry *dentry,
			      int dir)
{
	struct list_head *head = mount_hashtable + hash(mnt, dentry);
	struct list_head *tmp = head;
	struct mount *p, *found = NULL;
	
	for (;;) {
		tmp = dir ? tmp->next : tmp->prev;
		p = NULL;
		if (tmp == head)//循环一圈未找到
			break;
		p = list_entry(tmp, struct mount, mnt_hash);//mnt_hash 链接到mount_hashtable
		if (&p->mnt_parent->mnt == mnt && p->mnt_mountpoint == dentry) {//p其实是参数mnt的子文件系统
			found = p;
			break;
		}
	}
	return found;
}


### 回答1: Linux中的mount命令用于将文件系统挂载到指定的挂载点上。它可以将本地文件系统、网络文件系统、CD-ROM等各种文件系统挂载到指定的目录上,使得这些文件系统中的文件可以被访问和操作。mount命令还可以用于查看已经挂载的文件系统信息,以及卸载已经挂载的文件系统。 ### 回答2: Linux中的mount命令是用于将文件系统挂载到指定的目录上。当我们在Linux中添加一个新的硬盘或分区时,需要通过mount命令将其挂载到系统中的某个目录上,以便我们可以访问其中的文件和文件夹。 使用mount命令需要指定要挂载的文件系统和挂载点。文件系统可以是硬盘、分区、光盘或网络共享等。挂载点是一个目录,我们可以在该目录下访问被挂载的文件系统。 要使用mount命令,可以按照以下格式输入: mount [-t 文件系统类型] 设备文件名 挂载点 其中,-t选项用于指定要挂载的文件系统类型,设备文件名指定要挂载的设备路径,挂载点指定要将设备挂载到系统中的哪个目录。 例如,要将/dev/sdb1分区挂载到/mnt目录下,可以使用以下命令: mount -t ext4 /dev/sdb1 /mnt 此命令将/dev/sdb1分区以ext4文件系统类型挂载到/mnt目录下。挂载完成后,我们可以通过/mnt目录访问/dev/sdb1分区中的文件和文件夹。 在Linux中,mount命令还可以通过其他选项来设置挂载行为,如读写权限、挂载点选项等。可以使用man mount命令查看mount命令的完整用法和选项。 总之,mount命令是Linux中重要的文件系统管理工具,它可以让我们在系统中方便地挂载和访问不同的文件系统。 ### 回答3: Linux中的mount是一个命令,用于将文件系统挂载到指定的目录上。通过mount命令,可以将磁盘分区、移动存储设备或者网络共享来访问和使用。 使用mount命令需要指定要挂载的设备和目标挂载点。一旦执行mount命令,文件系统上的数据就可以通过目标挂载点进行访问和操作。挂载后,文件系统在目标挂载点下形成一个树状结构,用户可以像访问本地文件系统一样访问和管理挂载的文件系统。 mount命令还可以用来查看系统中已挂载的文件系统。通过输入mount命令,系统会显示所有已挂载的文件系统以及它们的挂载点、设备等信息。 在Linux中,挂载是将外部存储设备或网络存储连接到文件系统以便进行使用的过程。通过挂载,我们可以将各种外部设备(如硬盘、U盘、SD卡等)和网络存储(如NFS、CIFS等)与系统的目录结构进行关联,使得这些设备和存储能够被认可和使用。 总之,mount命令在Linux中扮演着非常重要的角色,它使得我们可以方便地挂载和使用各种外部设备和网络存储,扩展了Linux系统的功能和灵活性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值