源码追踪—-内核挂载文件系统的流程

背景

一个文件系统,只有挂载到内存中目录树下的一个目录,进程才能访问这个文件系统。

启动流程


我们都知道,系统的启动流程中
先加载uboot,负责硬件的基本初始化;
然后设置env,这里的env是全局env,在kernel/rootfs加载过程中都能使用;
加载内核到内存中,跳转到内核的入口
最后挂载根文件系统

mount命令需要的参数

mount -t fstype [-o optation] dev_name filedir

        所以,mount 需要知道挂载的文件系统类型,挂载参数,挂载目录,我们的目标是去获取mount所需要的所有参数。
        这里会引出一个问题,挂载,必须要有目录才能进行,加载内核后,没挂载根文件系统之前,哪里来的目录去挂载根文件系统呢?
        这里引出一个新概念,隐藏的根文件系统,即,内核启动时,自行创建/使用ramdiskfs的文件系统。

ramdisk

目录
以t3代码为例,ramdisk目录如下

        

        目录已经在上一步解决了,接下来看挂载参数包括对元数据的处理, fstype怎么来?

fstype
        常用的嵌入式 文件系统类型包括ext4、ubifs、yaffs2等,每种文件系统类型都各有优缺点,ext4介绍请查看培训内容《存储介质和文件系统.pptx》
        制作文件系统时,使用特定的制作工具,将做好的文件系统目录,打包制作成对应的格式,这是编译阶段就做好的。其实在加载的时候,内核会一个个文件系统类型尝试挂载,直到遍历完毕或成功挂载,后面追踪源码的时候会说明。

dev_name
        dev_name是文件系统镜像所在的分区,在挂载的时候怎么获取,总不能也一个个分区去遍历把。 这里其实是可以通过env分区获取,前面介绍env是整个开机流程都可以获取的。



        到这一步,挂载点有了,挂载参数有了,挂载真实的根文件系统,任务下放给1号进程,开机结束。










开始源码分析

总结上面,挂载分为三个大步骤

  参数解析      

        在加载内核的时候,执行了bootcmd。在bootcmd中,会将一些变量设置为环境变量。(详情查看《uboot环境变量》《uboot启动流程》两个章节)

挂载隐藏根文件系统


开始介绍挂载隐藏根文件系统

  虚拟文件系统

        上图,包括7个函数,其中四个函数为虚拟文件系统的四大数据对象(详情查看虚拟文件系统章节),分别是初始化目录项、索引节点、文件、超级块。除了这四个之外还有初始化最大文件句柄数,也就是每个进程可以打开的最多的文件数。一般默认是1024,可以通过ulimit指令查看

    

初始化挂载点和初始化块设备缓存

这里详细追踪一下初始化挂载点,其实就是构建一个挂载树

        第4行,创建高速缓存,用于存储struct mount结构体类型的对象。struct mount描述挂载点,包含了挂载点的路径、挂载的文件系统类型、挂载的文件系统对象等。
        第5行,指向哈希表的指针,用于存储挂载点的信息。
        第6行,指向哈希表的指针,用于存储挂载点路径饿对应的struct mount对象的指针。
        第8行,初始化kernfs文件系统。kernfs用于向内核提供对象的信息和控制接口,通常挂载在/sys目录下的kernfs子目录中,可以通过此目录下的文件来获取或修改内核对象的信息和属性。
        第9行,初始化sysfs文件系统,用于向用户空间提供内核对象的信息和控制接口。通过挂载在/sys目录下。在rootfs之前,说明此时sysfs可以使用
        第10行,初始化共享内存系统(shmemfs),用于向用户空间提供共享内存。通常挂载在/dev/shm目录下。
        第11行,初始化隐藏的的rootfs。
        第12行,初始化文件系统的挂载点和超级块。


这里举例 sysfs 向内核注册一个sysfs文件系统类型

在继续深入追踪,

init_rootfs

        我们一共做了哪些操作,初始化虚拟文件系统(查看2 虚拟文件系统的数据结构章节)的四个对象,超级块、索引节点、目录项、文件;然后注册并挂载了与内核交互的sysfs、kernelfs、shemfs,并且初始化了rootfs。到了这一步,我们所需要的挂载树基本构建完成,。

总结:

注册并挂载sysfs tmpfs rootfs,构建出mount tree,为rootfs创建super_block,root dentry,root inode并填充super_block,为后续其他文件系统的挂载提供基础



还差最后一步,挂载点,需要把这个隐藏文件系统挂载起来

初始化挂载点

挂载,需要一个挂载点还需要配置工作所需要的环境,比如命名空间,会话,工作目录,最顶级的父进程
1、挂载一个名为rootfs的文件系统
2、创建一个命名空间(命名空间,用来隔离资源)
3、根据挂载点获取到真实挂载点(真是挂载点,是指整个系统中的挂载点,而不是当前挂载的文件系统中的挂载点)
4、配置真实挂载点的命名空间
5、设置0号线程的命名空间
6、设置文件系统路径
7、把0号线程的当前工作目录,设置为rootfs文件系统的根目录
8、把0号线程的根目录,设置为roofs文件系统的根目录


到这一步,虚拟文件系统其实已经构建完毕了,接下来是真实存在的,镜像中rootfs分区中的文件系统的挂载




继续

挂载真实文件系统


        根文件系统挂载流程如下图所示,拆分亮哥步骤,第一个是挂载,第二个是启动1号进程
挂载又分为两种,一种是启用randisk,一种是不启用ramdisk

        rest_init函数启用了一个内核线程kernel_thread(kernel_init, NULL, CLONE_FS);
内核线程负责初始化内存管理、进程调度、文件系统,在初始文件系统时,会自动调用rootfs_initcall函数
        先介绍启用ramdisk的流程,rootfs_initcall(populate_rootfs)会将cpio包给解压到对应的内存上。
        如果没启用ramdisk,rootfs_initcall(default_rootfs);这里只是创建了/dev/ /root两个目录和/dev/console 一个文件。

        接下来为console创建标准输入、标准输出、标准错误文件描述符,也就是0 1 2。到这里,就可以通过console口对设备进行交互了


        第三步,准备根文件系统,如果是ramdisk,它已经具备了事前准备的所有工作,开始执行根文件系统中的init进程,也就是ramdisk中的init进程

到此,ramdisk方式的根文件系统挂载结束


对于非ramdisk方式,
1、探测块设备是否准备完毕(确保设备已经注册到设备模型中,并可以进行后续的操作)
2、如果存在节点是/dev/mtdxxx,说明存储设备是flash,执行mount_block_root函数,函数中会查找系统中支持的所有格式的文件系统,并一个个尝试挂载,直到挂载成功或遍历完成。
3、如果是emmc,则执行mount_root,这里创建块设备文件/dev/root,然后执行mount_block_root把根文件系统挂载到目录/root下
4、挂载devtmpfs文件系统
5、把当前工作目录移动到文件系统的根目录
6、跟文件系统重新挂载到根目录下
7、把1号线程的根目录设置为根文件系统的根目录
到此,根文件系统挂载完成

        最后一步,执行根文件系统的1号进程,init。这里可能是systemd sysvinit upstart(查看章节3 systemd)


结束

转载请标明出处 源码追踪—-内核挂载文件系统的流程-优快云博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值