| 日期 | 内核版本 | 架构 |
|---|---|---|
| 2022-10-09 | Linux5.4.200 | ARM64 |
前言
前不久项目上遇到了rootfs无法挂载的问题,就着这个问题顺便了解一下rootfs的挂载流程,总结于此分享给大家。
首先帮大家复习几个概念。
| 术语 | 解析 |
|---|---|
rootfs |
根文件系统,对应/目录节点。分为虚拟rootfs和真实rootfs:虚拟rootfs由内核自己创建和加载,仅仅存在于内存之中,其文件系统是tmpfs类型或者ramfs类型真实rootfs则是指根文件系统存在于存储设备上,内核在启动过程中会在虚拟rootfs上挂载这个存储设备,然后将/目录节点切换到这个存储设备上,这样存储设备上的文件系统就会被作为根文件系统使用 |
ramdisk |
将系统一部分内存区域实现为/dev/ram,把/dev/ram作为作为一个存储设备,最终将根目录切换到/dev/ram的挂载,实现将/dev/ram作为根文件系统的目的。使用 ext2格式的文件系统 |
initramfs |
kernel 2.5中引入的技术,在内核镜像中附加一个cpio包,这个cpio包中包含了一个小型的文件系统,当内核启动时,内核将这个 cpio包解开,并且将其中包含的文件系统释放到rootfs中,内核中的一部分初始化代码会放到这个文件系统中,作为用户层进程来执行。 |
调用关系链
下面总结了一下rootfs挂载的调用过程:
1 | start_kernel |
在挂载rootfs前会先挂载sysfs,确保sysfs能够完整的记录下设备驱动模型。
sysfs_init()完成注册和挂载sysfs文件系统的功能;init_rootfs()负责注册rootfs,init_mount_tree()负责挂载rootfs,并将init_task的命名空间与之联系起来。
所以在了解rootfs的挂载之前,我们先学习一下Linux文件系统初始化以及sysfs如何挂载。
Linux文件系统初始化
展开vfs_caches_init来看。
1 | void __init vfs_caches_init(void) |
-
vfs_caches_init()首先建立并初始化:- 目录hash表 -
dentry_hashtable - 索引节点hash表 -
inode_hashtable - 注:Linux使用哈希表存储目录和索引节点,以提高目录和索引节点的查找效率
- 目录hash表 -
-
设置内核可以打开的最大文件数
-
通过
mnt_init完成sysfs和rootfs文件系统的注册和挂载
sysfs挂载流程
sysfs负责记录Linux设备驱动模型.
https://elixir.bootlin.com/linux/v5.4.200/source/fs/namespace.c#L3766
1 | void __init mnt_init(void) |
mnt_init()调用sysfs_init()注册并挂载sysfs文件系统,然后调用kobject_create_and_add()创建fs目录。
https://elixir.bootlin.com/linux/v5.4.200/source/fs/sysfs/mount.c#L97
1 | int __init sysfs_init(void) |
第一步:创建根节点sysfs根节点。
第二步:building block of kernfs hierarchy,每一个节点都代表一个kernfs_node.
第三步:sysfs_init()调用register_filesystem()注册文件系统类型sysfs_fs_type,并加入到全局单链表file_systems中。
sysfs_fs_type定义如下,.mount成员函数负责超级块、根目录和索引节点的创建和初始化工作.
1 | static struct file_system_type sysfs_fs_type = { |
通过以上步骤,sysfs文件系统在VFS中的视图如下:
挂载点指向超级块和根目录;超级块处在super_blocks单链表中,并且链接起所有属于该文件系统的索引节点;根目录’/'和目录"fs"指向各自的索引节点;为了提高查找效率,索引节点保存在hash表中。
rootfs挂载过程
mnt_init()调用init_rootfs()注册文件系统类型rootfs_fs_type,并加入到全局单链表file_systems中。
rootfs_fs_type定义如下,mount成员函数负责超级块、根目录和索引节点的建立和初始化工作。
1 | struct file_system_type rootfs_fs_type = { |
-
init_mount_tree()调用vfs_kern_mount()挂载rootfs文件系统. -
init_mount_tree()创建命名空间,并设置该命名空间的挂载点为rootfs的挂载点,同时将rootfs的挂载点链接到该命名空间的双向链表中。 -
init_mount_tree()设置init_task的命名空间,同时调用set_fs_pwd()和set_fs_root()设置init_task任务的当前目录和根目录为rootfs的根目录’/’
1 | static void __init init_mount_tree(void) |
通过以上分析,我们发现sysfs和rootfs的区别在于:虽然系统同时挂载了sysfs和rootfs文件系统,但是只有rootfs处于init_task进程的命名空间内,也就是说系统启动和初始化阶段使用的是rootfs文件系统。
总结
sysfs和rootfs在VFS中的视图如下:
- Linux文件系统在初始化时,同时挂载了
sysfs和rootfs文件系统 - 只有
rootfs处于进程的命名空间中,且进程的root目录和pwd目录都指向rootfs的根目录 - Linux的
VFS已经准备好了根目录,用户可以使用系统调用对VFS树进行扩展


