每个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中,但是目前这个函数的具体实现并未找到,明天再说。。。
写了一下午,发现自己还完全没有追内核的能力。。。只能看看大佬的文章学习了。
参考资料
明天把上面这两篇专栏系统看一看。