日期 | 内核版本 | 架构 |
---|---|---|
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_typ
e定义如下,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
树进行扩展