linux文件系统初始化过程(4)---加载initrd(中)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 一、目的     上文详细介绍了CPIO格式的initrd文件,本文从源代码角度分析加载并解析initrd文件的过程。     initrd文件和linux内核一般存储在磁盘空间中,在系统启动阶段由bootload负责把磁盘上的内核和initrd加载到指定的内存空间中;然后,再由内核读取和解析initrd文件,在VFS(目前只有rootfs的根目录)中新建目录、常规文件、符号链接文件以及特殊文件;这样VFS就从根目录"/"成长为一棵枝繁叶茂的大树了。

一、目的

    上文详细介绍了CPIO格式的initrd文件,本文从源代码角度分析加载并解析initrd文件的过程。

    initrd文件linux内核一般存储在磁盘空间中,在系统启动阶段由bootload负责把磁盘上的内核和initrd加载到指定的内存空间中;然后,再由内核读取和解析initrd文件,在VFS(目前只有rootfs的根目录)中新建目录、常规文件、符号链接文件以及特殊文件;这样VFS就从根目录"/"成长为一棵枝繁叶茂的大树了

 

二、函数调用过程

 

    initrd详细的加载过程在init/initramfs.c中实现的,为了更好的理解加载过程,我们给出了关键函数的调用关系图1。这里需要注意下,由于使用roofs_initcall()宏在initcallroofs段中注册了populate_rootfs()函数,因此在执行do_initcalls()函数时会隐示调用populate_rootfs()。


                                图1

三、initcall简介

 

    linux在代码段中定义了一个特殊的段initcall,该段中存放的都是函数指针;linux初始化阶段调用do_initcalls()依次执行该段的函数。关于该段的详细信息可以参见vmlinux.lds.S链接脚本。

    用户可以调用以下一组宏在initcall段中注册函数指针;initcall段分为initcall0-initcall78个等级,initcall0段的优先级最高,initcall7段的优先级最低,优先级高的段最先被执行;initcallrootfs段优先级介于56之间。

#define __define_initcall(fn, id) \
179     static initcall_t __initcall_##fn##id __used \
180     __attribute__((__section__(".initcall" #id ".init"))) = fn

 

187 #define early_initcall(fn)          __define_initcall(fn, early)
      
196 #define pure_initcall(fn)           __define_initcall(fn, 0)
        
198 #define core_initcall(fn)           __define_initcall(fn, 1)
199 #define core_initcall_sync(fn)      __define_initcall(fn, 1s)
200 #define postcore_initcall(fn)       __define_initcall(fn, 2)
201 #define postcore_initcall_sync(fn)  __define_initcall(fn, 2s)
202 #define arch_initcall(fn)           __define_initcall(fn, 3)
203 #define arch_initcall_sync(fn)      __define_initcall(fn, 3s)
204 #define subsys_initcall(fn)         __define_initcall(fn, 4)
205 #define subsys_initcall_sync(fn)    __define_initcall(fn, 4s)
206 #define fs_initcall(fn)             __define_initcall(fn, 5)
207 #define fs_initcall_sync(fn)        __define_initcall(fn, 5s)
208 #define rootfs_initcall(fn)         __define_initcall(fn, rootfs)
209 #define device_initcall(fn)         __define_initcall(fn, 6)
210 #define device_initcall_sync(fn)    __define_initcall(fn, 6s)
211 #define late_initcall(fn)           __define_initcall(fn, 7)
212 #define late_initcall_sync(fn)      __define_initcall(fn, 7s)

  用户使用不同优先级的initcall宏可以很方便的在linux代码中注册函数指针;将这些函数指针存储在相应的initcall段中;最终,由do_initcalls()按照优先级依次执行段中的函数,具体的代码实现如下:

715 static initcall_t *initcall_levels[] __initdata = {
716     __initcall0_start,
717     __initcall1_start,
718     __initcall2_start,
719     __initcall3_start,
720     __initcall4_start,
721     __initcall5_start,
722     __initcall6_start,
723     __initcall7_start,
724     __initcall_end,
725 };

678 int __init_or_module do_one_initcall(initcall_t fn)
679 {
681     int ret;
686     ret = fn();
    }

739 static void __init do_initcall_level(int level)
740 {
742     initcall_t *fn;
            ... 
751     for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
752         do_one_initcall(*fn);
753 }
754 
755 static void __init do_initcalls(void)
756 {
757     int level;
758 
759     for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
760         do_initcall_level(level);
761 }

回到加载initrd这个话题中,在init/initram.c的最后使用rootfs_initcall宏注册了populate_rootfs()函数;基于以上分析,我们知道这里就是加载initrd文件的入口,下面就开始分析该函数的功能。

627 rootfs_initcall(populate_rootfs);

四、加载initrd文件

 

    系统启动阶段,bootloadinitrd加载到内存起始地址为initrd_start,结束地址为initrd_end的内存中。

    populate_rootfs()调用unpack_to_rootfs()从内存中读取并解析initrd文件;根据CPIO的格式我们知道initrd文件是由很多个段组成,且段中又是由文件头、文件名和文件体组成,因此该解析程序可以使用了状态机原理处理initrd文件。

    解析程序定义了以下8种状态:Start(初始状态)Collect(获取符号链接文件信息状态)GotHeader(获取文件头信息状态)SkipIt(跳过该段状态)GotName(获取文件名并新建文件状态)CopyFile(写文件状态)GotSymlink(新建符号链接文件状态)Reset(终止状态)。

376 static __initdata int (*actions[])(void) = {
377     [Start]     = do_start,
378     [Collect]   = do_collect,
379     [GotHeader] = do_header,
380     [SkipIt]    = do_skip,
381     [GotName]   = do_name,
382     [CopyFile]  = do_copy,
383     [GotSymlink]    = do_symlink,
384     [Reset]     = do_reset,
385 };

 

    为了直观理解initrd文件的解析过程,下面给出状态机跳转图2。

    从图中可以看出将文件分为符号链接和非符号链接两种情况处理,这是因为符号链接文件是一种特殊的文件,只有第一个符号链接文件的inode存储的是真实数据,而其他符号链接文件inode中存储的是第一个符号链接文件的路径名,因此需要把第一个符号链接文件的路径名缓存起来,缓存的数据结构是hash表,所以在处理符号链接文件时多了一些hash表的操作,因此分为了符号链接文件和非符号链接文件这两种情况来处理。

    initrd文件的详细解析过程如下:

    1S0:初始状态,初始化一些全局变量;

    2S1:获取符号链接文件的文件头和文件体;

    3S2:根据CPIO格式的定义,获取文件头信息;

    4S3:跳过当前CPIO格式的段,继续处理下一个段;

    5S4:获取文件名,并在VFS中新建文件;

    6S5:将文件内容写入到新建文件中;

    7S6:新建符号链接文件;

    8S7:处理完当前CPIO格式的段,继续一个段的处理。

 

    从图中还可以看出,由于目录文件和特殊文件没有文件内容,因此跳过了S5状态,直接进入S3状态。

                                                                     图2

五、总结

 

    通过以上分析,程序就可以成功解析initrd文件,并使用sys_dir()、sys_open()、sys_mknod()、sys_symlink()等系统调用新建目录、常规文件、特殊文件和符号链接文件了。此时,VFS从只有根目录"/"成长为了一棵内容丰富的大树。

 

相关文章
|
1月前
|
Ubuntu Linux 开发者
Ubuntu20.04搭建嵌入式linux网络加载内核、设备树和根文件系统
使用上述U-Boot命令配置并启动嵌入式设备。如果配置正确,设备将通过TFTP加载内核和设备树,并通过NFS挂载根文件系统。
95 15
|
2月前
|
Ubuntu Unix Linux
Linux网络文件系统NFS:配置与管理指南
NFS 是 Linux 系统中常用的网络文件系统协议,通过配置和管理 NFS,可以实现跨网络的文件共享。本文详细介绍了 NFS 的安装、配置、管理和常见问题的解决方法,希望对您的工作有所帮助。通过正确配置和优化 NFS,可以显著提高文件共享的效率和安全性。
233 7
|
2月前
|
存储 运维 监控
Linux--深入理与解linux文件系统与日志文件分析
深入理解 Linux 文件系统和日志文件分析,对于系统管理员和运维工程师来说至关重要。文件系统管理涉及到文件的组织、存储和检索,而日志文件则记录了系统和应用的运行状态,是排查故障和维护系统的重要依据。通过掌握文件系统和日志文件的管理和分析技能,可以有效提升系统的稳定性和安全性。
57 7
|
2月前
|
安全 Linux 数据安全/隐私保护
深入Linux操作系统:文件系统和权限管理
在数字世界的海洋中,操作系统是连接用户与硬件的桥梁,而Linux作为其中的佼佼者,其文件系统和权限管理则是这座桥梁上不可或缺的结构。本文将带你探索Linux的文件系统结构,理解文件权限的重要性,并通过实际案例揭示如何有效地管理和控制这些权限。我们将一起航行在Linux的命令行海洋中,解锁文件系统的奥秘,并学习如何保护你的数据免受不必要的访问。
|
3月前
|
存储 运维 监控
深入Linux基础:文件系统与进程管理详解
深入Linux基础:文件系统与进程管理详解
107 8
|
3月前
|
存储 Linux 文件存储
Linux文件系统
Linux文件系统 一切皆文件 在Linux中,“一切皆文件”的概念意味着系统中的所有资源,包括硬件设备、目录及进程等,均被视为文件。这种设计简化了操作和管理,具体包括: 普通文件:存储数据的常规文件。 目录文件:包含其他文件和子目录的文件。 进程文件:在/proc目录下代表系统中运行的进程。 设备文件:位于/dev目录,代表硬件设备。 网络字节流套接字文件:用于网络通信的数据流。 链接文件:指向另一个文件的符号链接或硬链接。 管道文件:用于进程间通信的文件。
75 7
|
6月前
|
缓存 NoSQL Linux
【Azure Redis 缓存】Windows和Linux系统本地安装Redis, 加载dump.rdb中数据以及通过AOF日志文件追加数据
【Azure Redis 缓存】Windows和Linux系统本地安装Redis, 加载dump.rdb中数据以及通过AOF日志文件追加数据
166 1
【Azure Redis 缓存】Windows和Linux系统本地安装Redis, 加载dump.rdb中数据以及通过AOF日志文件追加数据
|
6月前
|
编解码 Linux 程序员
深度探索Linux操作系统 —— 构建根文件系统2
深度探索Linux操作系统 —— 构建根文件系统
70 12
|
5月前
|
存储 Linux 索引
Linux 下最主流的文件系统格式——ext
【9月更文挑战第8天】硬盘被划分为若干相同大小的块(Block),默认大小为4K,便于灵活管理文件数据。文件数据分散存放于这些块中,提高了数据添加、删除和插入的便利性。
|
6月前
|
Linux Shell 网络安全
深度探索Linux操作系统 —— 构建根文件系统1
深度探索Linux操作系统 —— 构建根文件系统
86 6