• Initramfs 原理和实践


    Linux系统启动时使用initramfs (initram file system), initramfs可以在启动早期提供一个用户态环境,借助它可以完成一些内核在启动阶段不易完成的工作。当然initramfs是可选的,Linux中的内核编译选项默认开启initrd。在下面的示例情况中你可能要考虑用initramfs。

    • 加载模块,比如第三方driver
    • 定制化启动过程 (比如打印welcome message等)
    • 制作一个非常小的rescue shell
    • 任何kernel不能做的,但在用户态可以做的 (比如执行某些命令)

    一个initramfs至少要包含一个文件,文件名为/init。内核将这个文件执行起来的进程作为main init进程(pid 1)。当内核挂载initramfs后,文件系统的根分区还没有被mount, 这意味着你不能访问文件系统中的任何文件。如果你需要一个shell,必须把shell打包到initramfs中,如果你需要一个简单的工具,比如ls, 你也必须把它和它依赖的库或者模块打包到initramfs中。总之,initramfas是一个完全独立运行的体系。

    另外initramfs打包的时候,要求打包成压缩的cpio档案。cpio档案可以嵌入到内核image中,也可以作为一个独立的文件在启动的过程中被GRUB load。

    Linux的initramrd img

    在/boot目录下的initrd.img-xxx (Ubuntu)或者initramfs-xxx.img (CentOS) 文件即为Linux用的initramfs文件。我们可以将其解压出来看看其目录结构,如下:

    # ls -l /boot/
    total 67408
    -rw-r--r-- 1 root root  1240067 Jul 13  2016 abi-4.4.0-31-generic
    -rw-r--r-- 1 root root  1247269 Aug 15  2017 abi-4.4.0-93-generic
    -rw-r--r-- 1 root root   189566 Jul 13  2016 config-4.4.0-31-generic
    -rw-r--r-- 1 root root   190364 Aug 15  2017 config-4.4.0-93-generic
    drwxr-xr-x 5 root root     4096 Jul  4 17:23 grub
    -rw-r--r-- 1 root root 21977388 Aug 24  2017 initrd.img-4.4.0-31-generic
    -rw-r--r-- 1 root root 22440248 Aug 24  2017 initrd.img-4.4.0-93-generic
    -rw------- 1 root root  3879360 Jul 13  2016 System.map-4.4.0-31-generic
    -rw------- 1 root root  3899015 Aug 15  2017 System.map-4.4.0-93-generic
    -rw------- 1 root root  6937248 Jul 13  2016 vmlinuz-4.4.0-31-generic
    -rw------- 1 root root  7000752 Aug 15  2017 vmlinuz-4.4.0-93-generic
    # initrd的文件类型是gzip压缩文件 #
    file /boot/initrd.img-4.4.0-93-generic /boot/initrd.img-4.4.0-93-generic: gzip compressed data, from Unix, last modified: Thu Aug 24 20:51:59 2017
    # cp /boot/initrd.img-4.4.0-93-generic .
    # 文件大小为22M # ls -lh initrd.img-4.4.0-93-generic -rw-r--r-- 1 root root 22M Jul 5 15:46 initrd.img-4.4.0-93-generic
    # 修改文件的后缀名,否则gzip工具无法识别 #
    mv initrd.img-4.4.0-93-generic initrd.img-4.4.0-93-generic.gz # 用gzip解压缩 # gzip -d initrd.img-4.4.0-93-generic.gz # 解压后的大小为57M # ls -lh initrd.img-4.4.0-93-generic -rw-r--r-- 1 root root 57M Jul 5 15:46 initrd.img-4.4.0-93-generic

    # 解压后的文件类型为cpio档案 #
    file initrd.img-4.4.0-93-generic initrd.img-4.4.0-93-generic: ASCII cpio archive (SVR4 with no CRC)

    # 将文件从cpio档案中copy出来 # cpio
    -idmv < initrd.img-4.4.0-93-generic . lib64 lib64/ld-linux-x86-64.so.2 ... lib/systemd lib/systemd/systemd-udevd 115997 blocks

    # 最终可以看到如下文件和目录结构,就是initramrd的结构 #
    ls bin conf etc init initrd.img-4.4.0-93-generic lib lib64 run sbin scripts

    可以看到initramfs和跟分区文件系统的雏形很像,只是它的大小不大,少了很多工具和库。有些内核模块就在其中,比如:/lib/modules/4.4.0-93-generic/kernel/。

    qemu中启动"Hello World" initramfs

    在前文“在qemu环境中用gdb调试Linux内核”中,已经准备了一个Linux启动环境,但是缺少initramfs。我们可以做一个最简单的Hello World initramfs,来直观地理解initramfs。

    Hello World的C程序如下,与普通的Hello World相比,加了一行while(1)。

    #include <stdio.h>
    
    void main()
    {
        printf("Hello World
    ");
        fflush(stdout);
        /* 让程序打印完后继续维持在用户态 */
        while(1);
    }

    编译helloworld.c程序

    # gcc -static -o helloworld -m32 helloworld.c
    • -static: On systems that support dynamic linking, this prevents linking with the shared libraries. //不让gcc动态链接shared libraries
    • -m32: Generate code for a 32-bit or 64-bit environment //在前文“在qemu环境中用gdb调试Linux内核”中Linux内核被编译成了32位架构,所以这里在gcc的选项中也编译成32位可执行程序

    在64位机器上编译成32位程序,可能会报错如下:

    In file included from /usr/include/stdio.h:27:0,
    from helloworld.c:2:
    /usr/include/features.h:374:25: fatal error: sys/cdefs.h: No such file or directory
    # include <sys/cdefs.h>
    ^
    compilation terminated.

    解决方案是安装libc6-dev-i386包。

    # apt-get install libc6-dev-i386

    打包initramfs文件

    # echo helloworld | cpio -o --format=newc > hwinitramfs

    在qemu中启动编译好的内核,把hwinitramfs指定为initrd,在-append参数中将init指定为helloworld。

    # qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd hwinitramfs -append "console=ttyS0 rdinit=helloworld" -nographic

    系统能成功启动到输出"Hello World",并且在用户态停住。结合前文“在qemu环境中用gdb调试Linux内核”,可以看到qemu虚机中运行的Linux系统已经成功挂载了initramfs, 在console日志中也能看到“Unpacking initramfs...”。

    参考

    Custom Initramfs

    GNU CPIO Manual

  • 相关阅读:
    linux设置开机服务自动启动
    Redis相关指令文档
    Redis配置文件详解
    redis在windows下的安装
    WIN2003+IIS6+FastCGI+PHP5.3的安装配置
    研究生生活点滴一
    数据链路层
    C++中的static成员
    在构造函数中调用构造函数
    Java和C++的不同
  • 原文地址:https://www.cnblogs.com/wipan/p/9269505.html
Copyright © 2020-2023  润新知