Linux:关于 mount 的一些细节

1. 前言

限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。

2. mount 的主要细节

mount 从系统调用 sys_mount() 发起,如 mount -t tmpfs cgroup /sys/fs/cgroup

sys_mount() /* fs/namespace.c */
	do_mount()
		do_new_mount()
			struct vfsmount *mnt;
			...

			type = get_fs_type(fstype); /* 查找 @fstype 字串对应的文件系统类型 */
			...
			mnt = vfs_kern_mount(type, sb_flags, name, data);
				struct mount *mnt;
				struct dentry *root; /* @type 文件系统 的 根目录 */

				...
				mnt = alloc_vfsmnt(name);
				...
				root = mount_fs(type, flags, name, data);
					struct dentry *root; /* @type 文件系统 的 根目录 */
					struct super_block *sb; /* @type 文件系统 super block */
					...
					
					root = type->mount(type, flags, name, data);
						shmem_mount() /* tmpfs 的 挂载接口 */
					...
					sb = root->d_sb;
					...

/* mm/shmem.c */
static struct dentry *shmem_mount(struct file_system_type *fs_type,
	int flags, const char *dev_name, void *data)
{
	return mount_nodev(fs_type, flags, data, shmem_fill_super);
}

struct dentry *mount_nodev(struct file_system_type *fs_type,
	int flags, void *data,
	int (*fill_super)(struct super_block *, void *, int))
{
	...
	/* 分配 super block 对象 */
	struct super_block *s = sget(fs_type, NULL, set_anon_super, flags, NULL);

	...
	error = fill_super(s, data, flags & SB_SILENT ? 1 : 0); /* shmem_fill_super(), ... */
	...
}

int shmem_fill_super(struct super_block *sb, void *data, int silent)
{
	struct inode *inode; /* tmpfs 根目录的 inode */
	struct shmem_sb_info *sbinfo; /* 特定于 tmpfs 的 super block 信息 */
	...

	/* Round up to L1_CACHE_BYTES to resist false sharing */
	sbinfo = kzalloc(max((int)sizeof(struct shmem_sb_info),
				L1_CACHE_BYTES), GFP_KERNEL);
	...
	sb->s_fs_info = sbinfo;

	...
	sb->s_magic = TMPFS_MAGIC;
	sb->s_op = &shmem_ops;
	...

	/* 创建 tmpfs 根目录的 inode */
	inode = shmem_get_inode(sb, NULL, S_IFDIR | sbinfo->mode, 0, VM_NORESERVE);
	if (!inode)
		goto failed;
	inode->i_uid = sbinfo->uid;
	inode->i_gid = sbinfo->gid;
	/* 创建 tmpfs 根目录对象 */
	sb->s_root = d_make_root(inode);
	...
}

static struct inode *shmem_get_inode(struct super_block *sb, const struct inode *dir,
				     umode_t mode, dev_t dev, unsigned long flags)
{
	struct inode *inode;
	...

	inode = new_inode(sb);
	if (inode) {
		inode->i_ino = get_next_ino(); /* 分配 inode 编号 */
		...
		inode->i_blocks = 0;
		inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
		inode->i_generation = get_seconds();
		...

		/* 根据 inode 的类型,设置对应的接口 */
		switch (mode & S_IFMT) {
		...
		case S_IFDIR: /* 这里的上下文是为 tmpfs 建立的 根目录 的 inode */
			inc_nlink(inode);
			/* Some things misbehave if size == 0 on a directory */
			inode->i_size = 2 * BOGO_DIRENT_SIZE;
			inode->i_op = &shmem_dir_inode_operations;
			inode->i_fop = &simple_dir_operations;
			break;
		...
		}
	}  else
		...
	return inode;
}

看下来,mount 的重点工作是:

1. 建立文件系统的 super block 。
2. 建立根目录,同时绑定根目录的接口。
   在例子中的上下文,是绑定 shmem_dir_inode_operations、simple_dir_operations 
   接口。

建立文件系统 super block,以及文件系统根目录和其接口设定,自然都是为了管理文件系统。super block 包含文件系统的很多信息,譬如关联的设备,最大 block 尺寸,等等,这里不详细展开。而文件系统根目录,首先是建立了文件系统起始路径再就是根目录接口的设定,其主要目的是用来创建目录和文件。先看文件创建的过程:

sys_open()
	do_sys_open()
		do_filp_open()
			path_openat()
				struct file *file;
				...
				file = get_empty_filp(); /* 新建 file 对象 */
				...
				do_last(nd, file, op, &opened)
					...
					/* 创建文件 inode,设置文件 file 对象 */
					lookup_open(nd, &path, file, op, got_write, opened)
						...
						/* Negative dentry, just create the file */
						if (!dentry->d_inode && (open_flag & O_CREAT)) {
							error = dir_inode->i_op->create(dir_inode, dentry, mode, open_flag & O_EXCL);
								shmem_create() /* 创建文件 inode 和 接口 */
						}
					...
					vfs_open(&nd->path, file, current_cred());
						do_dentry_open(file, d_backing_inode(dentry), NULL, cred)
							...
							f->f_inode = inode; /* 绑定文件 file 的 inode */
							...
							/* 绑定文件 file 对象的接口 &simple_dir_operations */
							f->f_op = fops_get(inode->i_fop); 
							...	

static int shmem_create(struct inode *dir, struct dentry *dentry, umode_t mode,
		bool excl)
{
	return shmem_mknod(dir, dentry, mode | S_IFREG, 0);
}

static int
shmem_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
{
	struct inode *inode;
	...

	inode = shmem_get_inode(dir->i_sb, dir, mode, dev, VM_NORESERVE);
	if (inode) {
		...
		d_instantiate(dentry, inode);
		...
	}
	return error;
	...
}

static struct inode *shmem_get_inode(struct super_block *sb, const struct inode *dir,
				     umode_t mode, dev_t dev, unsigned long flags)
{
	struct inode *inode;
	...

	inode = new_inode(sb);
	if (inode) {
		inode->i_ino = get_next_ino(); /* 分配 inode 编号 */
		...
		switch (mode & S_IFMT) {
		...
		case S_IFREG: /* 文件类型 inode 接口设定 */
			inode->i_mapping->a_ops = &shmem_aops;
			inode->i_op = &shmem_inode_operations;
			inode->i_fop = &shmem_file_operations;
			mpol_shared_policy_init(&info->policy,
						 shmem_get_sbmpol(sbinfo));
			break;
		...
		}
	}
	...
}

再看目录创建的过程:

sys_mkdir()
	sys_mkdirat()
		vfs_mkdir(path.dentry->d_inode, dentry, mode)
			dir->i_op->mkdir(dir, dentry, mode)
				shmem_mkdir()
					shmem_mknod(dir, dentry, mode | S_IFDIR, 0)
						struct inode *inode;
						...
						/* 创建 目录 的 inode ,过程参考前面 tmpfs 的 挂载过程 */
						inode = shmem_get_inode(dir->i_sb, dir, mode, dev, VM_NORESERVE);
						if (inode) {
							...
							dir->i_size += BOGO_DIRENT_SIZE;
							dir->i_ctime = dir->i_mtime = current_time(dir);
							d_instantiate(dentry, inode);
							dget(dentry); /* Extra count - pin the dentry in core */
						}

到此,mount 的主要过程及相关细节已经分析完毕。

简单小结一下,mount 的主要目的就是 建立 super block根目录,然后绑定的根目录接口又可以进行目录和文件的创建和接口绑定,目录下的目录的接口又可以创建目录和文件,如此层层递推。

posted @ 2025-04-07 10:32  JiMoKuangXiangQu  阅读(24)  评论(0)    收藏  举报