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\n");
- 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 https://wiki.gentoo.org/wiki/Custom_Initramfs
GNU CPIO Manual http://www.gnu.org/software/cpio/manual/cpio.html
来源: https://www.cnblogs.com/wipan/p/9269505.html