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


    转自:https://blog.csdn.net/luomoweilan/article/details/18324379

    一、目的

        上文详细介绍了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-initcall7这8个等级,initcall0段的优先级最高,initcall7段的优先级最低,优先级高的段最先被执行;initcallrootfs段优先级介于5和6之间。

    1.  
      #define __define_initcall(fn, id)
    2.  
      179 static initcall_t __initcall_##fn##id __used
    3.  
      180 __attribute__((__section__(".initcall" #id ".init"))) = fn
    1.  
      187 #define early_initcall(fn) __define_initcall(fn, early)
    2.  
       
    3.  
      196 #define pure_initcall(fn) __define_initcall(fn, 0)
    4.  
       
    5.  
      198 #define core_initcall(fn) __define_initcall(fn, 1)
    6.  
      199 #define core_initcall_sync(fn) __define_initcall(fn, 1s)
    7.  
      200 #define postcore_initcall(fn) __define_initcall(fn, 2)
    8.  
      201 #define postcore_initcall_sync(fn) __define_initcall(fn, 2s)
    9.  
      202 #define arch_initcall(fn) __define_initcall(fn, 3)
    10.  
      203 #define arch_initcall_sync(fn) __define_initcall(fn, 3s)
    11.  
      204 #define subsys_initcall(fn) __define_initcall(fn, 4)
    12.  
      205 #define subsys_initcall_sync(fn) __define_initcall(fn, 4s)
    13.  
      206 #define fs_initcall(fn) __define_initcall(fn, 5)
    14.  
      207 #define fs_initcall_sync(fn) __define_initcall(fn, 5s)
    15.  
      208 #define rootfs_initcall(fn) __define_initcall(fn, rootfs)
    16.  
      209 #define device_initcall(fn) __define_initcall(fn, 6)
    17.  
      210 #define device_initcall_sync(fn) __define_initcall(fn, 6s)
    18.  
      211 #define late_initcall(fn) __define_initcall(fn, 7)
    19.  
      212 #define late_initcall_sync(fn) __define_initcall(fn, 7s)

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

    1.  
      715 static initcall_t *initcall_levels[] __initdata = {
    2.  
      716 __initcall0_start,
    3.  
      717 __initcall1_start,
    4.  
      718 __initcall2_start,
    5.  
      719 __initcall3_start,
    6.  
      720 __initcall4_start,
    7.  
      721 __initcall5_start,
    8.  
      722 __initcall6_start,
    9.  
      723 __initcall7_start,
    10.  
      724 __initcall_end,
    11.  
      725 };
    12.  
       
    13.  
      678 int __init_or_module do_one_initcall(initcall_t fn)
    14.  
      679 {
    15.  
      681 int ret;
    16.  
      686 ret = fn();
    17.  
      }
    18.  
       
    19.  
      739 static void __init do_initcall_level(int level)
    20.  
      740 {
    21.  
      742 initcall_t *fn;
    22.  
      ...
    23.  
      751 for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
    24.  
      752 do_one_initcall(*fn);
    25.  
      753 }
    26.  
      754
    27.  
      755 static void __init do_initcalls(void)
    28.  
      756 {
    29.  
      757 int level;
    30.  
      758
    31.  
      759 for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
    32.  
      760 do_initcall_level(level);
    33.  
      761 }

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

     

    627 rootfs_initcall(populate_rootfs);

     

    四、加载initrd文件

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

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

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

    1.  
      376 static __initdata int (*actions[])(void) = {
    2.  
      377 [Start] = do_start,
    3.  
      378 [Collect] = do_collect,
    4.  
      379 [GotHeader] = do_header,
    5.  
      380 [SkipIt] = do_skip,
    6.  
      381 [GotName] = do_name,
    7.  
      382 [CopyFile] = do_copy,
    8.  
      383 [GotSymlink] = do_symlink,
    9.  
      384 [Reset] = do_reset,
    10.  
      385 };

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

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

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

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

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

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

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

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

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

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

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

     

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

                                                                         图2

     

     

    五、总结

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

     

    版权声明:

        原创作品,如非商业性转载,请注明出处;如商业性转载出版,请与作者联系。

    【作者】张昺华
    【大饼教你学系列】https://edu.csdn.net/course/detail/10393
    【新浪微博】 张昺华--sky
    【twitter】 @sky2030_
    【微信公众号】 张昺华
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    ASP.NET 静态化
    一周死磕fastreport ----ASP.NET (二)
    一周死磕fastreport ----ASP.NET (一)
    前台替换用户名部分转换为*显示
    截取部分字符信息
    win10家庭版设置移动热点出现“我们无法设置移动热点”
    云服务器配置出现的问题 2
    云服务器配置出现的问题 1
    关于使用jquery评论插件...
    $(selector).each() 和$each() 的区别
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/13742102.html
Copyright © 2020-2023  润新知