每个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系统调用初探
明天把上面这两篇专栏系统看一看。