linux文件系统初探--Day2(1)

每个mount过程都会存在一个超级块与之对应,超级块中包含了相关的信息。在写代码之前,首先搞清楚mount过程是很有必要的。

以下部分参考《深入Linux内核架构》第八章。

vfsmount和super_block结构初步

在day1中,我们完成了文件系统的注册,也就是说我们将samplefs的信息通知内核,让内核知道有这么个东西,所以在注册过程中只需要向file_systems链表添加一个对象就可以。但是mount过程相对来说比较复杂,下面我们详细了解。

Unix采用了一种单一的文件系统结构,新的文件系统可以嵌套其中。文件系统mount的目录叫做装载点,在将文件系统mount到一个目录时,装载点的内容被替换为即将装载的文件系统的相对根目录的位置。每个装载的文件系统都对应了一个vfsmount实例。

struct vfsmount {
      struct dentry *mnt_root;
      struct super_block *mnt_sb;
      int mnt_flags;
}

mnt_root中保存了当前文件系统根目录的dentry,mnt_sb中保存了当前文件系统对应的超级块。在早期内核中,vfsmount中还存在很多链表元素,目前这些成员都被转移到struct mount中,暂不对其进行更深入的分析。

在装载文件系统前,首先需要创建超级块,之后对超级块进行读取,装载工作才算正式开始,struct super_block的结构是这样的:

struct super_block {

	[...snipped...]

	unsigned char		s_blocksize_bits;
	unsigned long		s_blocksize;
	loff_t			s_maxbytes;	/* Max file size */
	struct file_system_type	*s_type;
	const struct super_operations	*s_op;

	[...snipped...]

	unsigned long		s_magic;
	struct dentry		*s_root;

	[...snipped...]

	void 			*s_fs_info;	/* Filesystem private info */

	[...snipped...]

	/* Granularity of c/m/atime in ns.
	   Cannot be worse than a second */
	u32		   s_time_gran;

	[...snipped...]
};

s_blocksize 和 s_blocksize_bits指定了文件系统的块长度。
s_type指向file_syetem_type的实例,保存了与文件系统有关的一般类型的信息。
s_root将超级块与全局根目录的dentry关联起来,而根目录的dentry又可以指向根目录的inode。一般用s_root检查文件系统是否已经装载,如果是NULL,则该文件系统只在内核内部可见,否则在用户空间中可见。
s_fs_info指向文件系统的私有信息。
s_op指向一个包含了函数指针的结构,提供了处理超级块的一般接口。具体的struct super_operations暂时不做介绍。

mount系统调用

mount系统调用在fs/namespace.c中实现:

SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
		char __user *, type, unsigned long, flags, void __user *, data)
{
	return ksys_mount(dev_name, dir_name, type, flags, data);
}

因此在5.4.89内核中,mount一个新文件系统的路径如下:

mount -> ksys_mount -> do_mount -> do_new_mount -> do_new_mount_fc -> vfs_create_mount, do_add_mount

我们发现在5.4.89内核中,在do_new_mount过程中,调用do_new_mount_fc,发现使用了一个fs_context新结构。

struct fs_context {
	const struct fs_context_operations *ops;
	struct mutex		uapi_mutex;	/* Userspace access mutex */
	struct file_system_type	*fs_type;
	void			*fs_private;	/* The filesystem's context */
	void			*sget_key;
	struct dentry		*root;		/* The root and superblock */
	struct user_namespace	*user_ns;	/* The user namespace for this mount */
	struct net		*net_ns;	/* The network namespace for this mount */
	const struct cred	*cred;		/* The mounter's credentials */
	struct fc_log		*log;		/* Logging buffer */
	const char		*source;	/* The source name (eg. dev path) */
	void			*security;	/* Linux S&M options */
	void			*s_fs_info;	/* Proposed s_fs_info */
	unsigned int		sb_flags;	/* Proposed superblock flags (SB_*) */
	unsigned int		sb_flags_mask;	/* Superblock flags that were changed */
	unsigned int		s_iflags;	/* OR'd with sb->s_iflags */
	unsigned int		lsm_flags;	/* Information flags from the fs to the LSM */
	enum fs_context_purpose	purpose:8;
	enum fs_context_phase	phase:8;	/* The phase the context is in */
	bool			need_free:1;	/* Need to call ops->free() */
	bool			global:1;	/* Goes into &init_user_ns */
};

我们发现,vfs_create_mount中原来回调fill_super函数的位置现在变成了一个dget(fc->root),所以我个人觉得调用fill_super的位置应该在vfs_get_tree中,但是目前这个函数的具体实现并未找到,明天再说。。。

写了一下午,发现自己还完全没有追内核的能力。。。只能看看大佬的文章学习了。

参考资料

Linux中的VFS实现 [二]
mount系统调用初探

明天把上面这两篇专栏系统看一看。

posted @ 2021-01-18 19:59  xinze  阅读(244)  评论(3编辑  收藏  举报