接前一篇文章:Linux内核有什么之块设备驱动有什么第三回 —— 邂逅的三个文件系统之一:devtmpfs
本文内容参考:
34 | 块设备(上):如何建立代理商销售模式?-趣谈Linux操作系统-极客时间
Linux内核——块设备总结_linux do_open-CSDN博客
【Linux驱动】块设备驱动(一)—— 注册块设备_创建块设备-CSDN博客
ext4 mount流程_ext4文件系统的mount option设置-CSDN博客
特此致谢!
上一回讲解了块设备驱动邂逅的第一个文件系统:devtmpfs。本回讲解块设备接下来邂逅的第二个文件系统,也是向这个块设备进行读写等操作,需要基于的主流文件系统,如ext2/3/4、XFS、F2FS等。
上回书讲过,块设备虽然与字符设备类似,也有打开、关闭、读写函数,但与字符设备不同,块设备常规操作不会直接打开/dev/下的块设备结点进行操作,而是会将此块设备(文件)mount到一个文件夹下面。
假设此块设备之前已被格式化为了某种文件系统的格式,以ext4为例。那么,mount操作实际调用的就是ext4文件系统相应的mount操作。来看一下ext4文件系统的mount流程。
上回书在讲devtmpfs的时候,讲到对devtmpfs文件系统的初始化是由devtmpfs_init函数完成的。再次贴出devtmpfs_init函数的代码,在drivers/base/devtmpfs.c中,如下:
/*
* Create devtmpfs instance, driver-core devices will add their device
* nodes here.
*/
int __init devtmpfs_init(void)
{
char opts[] = "mode=0755";
int err;
mnt = vfs_kern_mount(&internal_fs_type, 0, "devtmpfs", opts);
if (IS_ERR(mnt)) {
pr_err("unable to create devtmpfs %ld\n", PTR_ERR(mnt));
return PTR_ERR(mnt);
}
err = register_filesystem(&dev_fs_type);
if (err) {
pr_err("unable to register devtmpfs type %d\n", err);
return err;
}
thread = kthread_run(devtmpfsd, &err, "kdevtmpfs");
if (!IS_ERR(thread)) {
wait_for_completion(&setup_done);
} else {
err = PTR_ERR(thread);
thread = NULL;
}
if (err) {
pr_err("unable to create devtmpfs %d\n", err);
unregister_filesystem(&dev_fs_type);
thread = NULL;
return err;
}
pr_info("initialized\n");
return 0;
}
与几乎所有的文件系统注册一样,在devtmpfs_init函数中会调用register_filesystem函数向Linux内核注册文件系统。devtmpfs文件系统类型描述符dev_fs_type定义如下(同文件中):
static struct file_system_type dev_fs_type = {
.name = "devtmpfs",
.mount = public_dev_mount,
};
那么与devtmpfs相类似和对应,在注册ext4文件系统的时候,也有其相对应的struct file_system_type实例。该实例为ext4_fs_type,在fs/ext4/super.c中,定义(更准确地说应该是初始化)如下:
static struct file_system_type ext4_fs_type = {
.owner = THIS_MODULE,
.name = "ext4",
.init_fs_context = ext4_init_fs_context,
.parameters = ext4_param_specs,
.kill_sb = ext4_kill_sb,
.fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP,
};
这里要说一下,老版本Linux内核代码中,ext4_fs_type的值是这样的:
static struct file_system_type ext4_fs_type = {
.owner = THIS_MODULE,
.name = "ext4",
.mount = ext4_mount,
.kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV,
};
可以看到,老版本的ext4_fs_type代码中多了对mount成员的赋值,为ext4_mount函数。而新版本中已经去掉了。而老版本代码的ext4_mount函数代码为:
static struct dentry *ext4_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data)
{
return mount_bdev(fs_type, flags, dev_name, data, ext4_fill_super);
}
而ext4_mount中所调用的mount_bdev函数,也在ext4乃至整个ext文件系统中找不到了。
说实在的,笔者对于内核这帮人没事闲的、老瞎改代码的行为是极其不满的。Linux内核源码本来就博大精深,其中很多内容需要花时间去深入研究。很多书或者博文将机制讲得比较透彻、也比较好理解了,但是再回来看新版内核代码,书或文章中所讲的内容已经没了,至于为什么去掉或者更改,就只能查阅www.kernel.org中的相应的内核提交说明。
以现在的ext4文件系统为例,刘超先生已经在其文章暨课程34 | 块设备(上):如何建立代理商销售模式?-趣谈Linux操作系统-极客时间中把老版本代码中的ext4的mount机制讲得比较透彻了,但是笔者一查新版内核(6.7版本),整个ext4_mount函数都没了。你就感觉,他们那个圈子想怎么变就怎么变、想怎么玩就怎么玩,不管大多数后来者和有志于入圈Linux文件系统尤其是ext文件系统的人。
这里,笔者就看看到底这帮维护者或贡献值为什么这样改变,这样做的好处和作用究竟是什么。访问以下链接:kernel/git/torvalds/linux.git - Linux kernel source tree,页面如下:
切换到“tree”选项下,页面如下:
进入到fs/ext4/super.c,页面如下:
切换到“log”选项下,页面如下:
找一下提交信息,看看ext4_mount函数是在什么时候被去掉的,在那次提交中被去掉的,原因究竟是什么。
经过仔细的定位查找,最终找到了具体的那一次提交:
这时间还挺早呢,距现在已经有2年多了。不过刘超的课程是2019年左右就上线了的,因此确实是在这次代码更新之前,还是老版本的代码。来看一下具体的提交说明,了解一下细节:
ext4: switch to the new mount api
ext4:切换至新的挂载api
Add the necessary functions for the fs_context_operations. Convert and rename ext4_remount() and ext4_fill_super() to ext4_get_tree() and ext4_reconfigure() respectively and switch the ext4 to use the new api.
为fs_context_operations添加了必要的函数。将ext4_remont()和ext4_fill_super()分别转换并重命名为ext4_get_tree()和text4_reconfigurate(),并将ext4切换为使用新的api。
One user facing change is the fact that we no longer have access to the entire string of mount options provided by mount(2) since the mount api does not store it anywhere. As a result we can't print the options to the log as we did in the past after the successful mount.
一个用户面临的变化是,我们不再能够访问mount提供的整个挂载选项字符串,因为挂载api不会将其存储在任何地方。因此,在成功挂载后,我们无法像过去那样将选项打印到日志中。
既然ext家族比较“蛋大”,非得玩点“幺蛾子”,而我们这个系列的主题又是块设备而非文件系统。因此,笔者不用ext4了、而是重新选择F2FS文件系统进行讲解,F2FS的代码还是按照老版内核的挂载机制来的。详情请看下回。