initrd解析

参考文献:

[1] initrd
[2] 内核文档
[3] 内核手册man initrd(4)
[4] linux 内核启动Initramfs与initrd 及其挂载
[5] rootfs的建立过程
[6] initramfs的起源
[7] initrd是如何释放到rootfs的
[8] 阿里一个写的好的博客
[9] initramfs与initrd的区别
[10] Linux2.6 内核的 Initrd 机制解析-阿里云开发者社区

概述

initrd主要被设计用来执行系统的两阶段启动,在initrd中,内核完成最简单的驱动设置,加载initrd中的基本模块。
initrd RAM磁盘最初是设计用来通过一个临时根文件系统来作为内核到最终的根文件系统之间的桥梁。
/dev/initrd是一个只读块设备,其主设备号是1,次设备号是250。该设备是一个在内核被启动前被bootloader用于初始化的RAM disk,内核使用/dev/initrd的内容用于两阶段启动。
当使用initrd引导时,系统典型的启动流程如下:

  1. BootLoader加载内核程序和/dev/initrd的内容到内存;
  2. 当内核启动时,内核解压并拷贝设备/dev/initrd中的内容到设备/dev/ram0,然后释放/dev/initrd使用的内存空间;
  3. 内核以读写方式挂载/dev/ram0设备作为初始根文件系统;
    在初始根文件系统中init(一般是一个脚本)被执行,init脚本中做基本的初始化操作,挂载真正的根文件系统,切换根文件系统,执行真正根文件系统中的/sbin/init
  4. 如果指出的正常根文件系统也是初始根文件系统(/dev/ram0),内核调到最后一步正常引导序列;
  5. 如果可执行文件/linuxrc在初始根文件系统存在,/linuxrc被以UID为0执行。(/linuxrc必须有可执行权限,且是任何有效的可执行文件或脚本)
  6. 如果/linuxrc不被执行或当/linuxrc终止时,正常根文件系统被挂载。(如果文件/linuxrc退出,而没有任何文件系统挂载到初始根文件系统,内核行为未定义)
  7. 如果正常根文件系统有一个目录/initrd,设备/dev/ram0被从/移动到/initrd。否则如果目录/initrd不存在,设备/dev/ram0解挂。(当从/移动到/initrd时,/dev/ram0不解挂,因此任何有/dev/ram0运行的进程可以保留。如果目录/initrd不存在,当/linuxrc退出时,由/dev/ram0运行的进程将退出,内核行为未定义)
  8. 正常引导序列(调用/sbin/init)在正常根文件系统中被执行。
    可以认为initrd的核心是其中的init脚本。

当使用initrd机制时,bootloader中的 initrd=filename, noinitrd, root=device-name 三个选项将影响内核的引导操作流程:
(1) initrd=filename
指定加载filename文件来作为/dev/initrd的内容。对于LILO(一个经典的bootloader),必须在配置文件/etc/lilo.config中使用该命令, 典型的情况是filename是一个gzip文件系统映像。
(2) noinitrd
该选项去能两阶段引导操作。内核执行正常引导序列,就好像/dev/initrd没有被初始化过一样。
root=device-name
指定被用于正常根文件系统的设备。对LILO来说,该选项在配置文件/etc/lilo.config中作为可选项使用。该选项指定的设备必须是一个有合适根文件系统的可引导设备。
initrd中必须要有init, 若初始化程序是其他名字,则要在cmdline中使用rdinit指定

Ubuntu的initrd

引导映像文件是/boot/initrd.img-4.4.0-21-generic
Linux2.4内核的initrd与2.6版的initrd不一样。
linux2.4内核的initrd的执行是作为内核启动的一个中间阶段,也就是说 initrd 的 /linuxrc 执行以后,内核会继续执行初始化代码。
2.6内核的initrd是内核启动后用其中的init进行一些最基本的初始化,模块挂载等操作,然后切换到真正的根文件系统。
2.4版中initrd是普通映像文件格式称为 image-initrd, 2.6版中是cpio格式称为 cpio-initrd。
image-initrd通常命名为ramdisk.img.gz,cpio-initrd通常命名为initrd.lz
但linux2.6内核支持两种格式的initrd,cpio格式从linux2.5 起开始引入,使用cpio工具生成,其核心文件是 /init。尽管 linux2.6 内核对 cpio-initrd和 image-initrd 这两种格式的 initrd 均支持,但对其处理流程有着显著的区别,下面分别介绍 linux2.6 内核对这两种 initrd 的处理流程。

cpio-initrd 的处理流程

  1. bootloader把内核及initrd文件加载到内存的特定位置。
  2. 内核判断initrd的文件格式,如果是cpio格式。
  3. 将initrd的内容释放到rootfs中。
  4. 执行initrd中的/init文件,执行到这一点,内核的工作全部结束,完全交给/init文件处理。

image-initrd的处理流程

  1. bootloader把内核及initrd文件加载到内存的特定位置。
  2. 内核判断initrd的文件格式,如果不是cpio格式,将其作为image-initrd处理。
  3. 内核将initrd的内容保存在rootfs下的/initrd.image文件中。
  4. 内核将/initrd.image的内容读入/dev/ram0设备中,也就是读入了一个内存盘中。
  5. 接着内核以可读写的方式把/dev/ram0设备挂载为初始的根文件系统。
  6. 如果/dev/ram0被指定为真正的根文件系统,那么内核跳至最后一步正常启动。
  7. 执行initrd上的/linuxrc文件,linuxrc通常是一个脚本文件,负责加载内核访问根文件系统必须的驱动, 以及加载根文件系统。
  8. /linuxrc执行完毕,常规根文件系统被挂载
  9. 如果常规根文件系统存在/initrd目录,那么/dev/ram0将从/移动到/initrd。否则/dev/ram0将被卸载。
  10. 在常规根文件系统上进行正常启动过程 ,执行/sbin/init。

systemd的initrd接口
(1)initrd应当被挂载到/run作为临时文件系统,挂载模式应该为mode=755, nodev
(2)如果可执行文件/run/initramfs/shutdown存在,在关机时systemd将使用它跳回initrd
(3)

有两种跟切换机制
Obsolete change_root mechanism and new pivot_root mechanism

  1. change_root

echo 0x301 >/proc/sys/kernel/real-root-dev

  1. pivot_root
    root=/dev/ram0 to trigger the pivot_root mechanism

dracut
dracut('dreIket) 是一个事件驱动的 initramfs 基础结构,它被用于从已安装的系统中通过拷贝工具和文件创建一个 initramfs 映像, 通常在 /usr/lib/dracut/modules.d目录中.见文档,
即dracut 一个可以用于生成initramfs的开源工具,另外一种是mkinitrd
但是Ubuntu和gentoo都没有使用dracut,倒也没有mkinitrd。
ubuntu下安装dracut
$ apt install dracut
相关命令
$ dracut # 根据当前rootfs构建 initramfs(/boot/initramfs-$(uname -r).img)
$ dracut --print-cmdline # 打印出内核cmdline
$ dracut --list-modules # 列出dracut模块
添加文件到initramfs
$ dracut --include /custom-content.conf /etc/custom-content.conf
生成initramfs时替换默认cmdline(但会保留自动检测到的cmdline)
$ dracut --kernel-cmdline “root=UUID=93b1fb15-99d0-4dcd-aef3-26d703459164 rootfstype=ext4 rootflags=rw,relatime,errors=remount-ro”
列出initramfs中的内容
$ lsinitrd /boot/initrd.img-4.19.68
$ lsinitramfs /boot/initrd.img-4.19.68
参考文档如何使用dracut创建一个initramfs

2.6以后的内核中initrd.img用cpio制作
制作cpio格式文件系统的方法如下:

$ gen_initramfs_list.sh ./Filesystem/ >filelist  # 其中Filesystem文件夹是由上一步解压出来的cpio文件系统目录
$ gen_init_cpio filelist >rootfs.cpio # 产生cpio文件
$ gzip rootfs.cpio # 压缩cpio生成cpio.gz文件

解压步骤:

$ mv initrd.img initrd.img.gz #  重命名initrd.img,(可以用file命令确认)
$ gzip -d initrd.img.gz # 解压initrd.img.gz
$ cpio -idmv < initrd.img # 解包initrd.img
注意:该解包一般解包到当前目录下,所以最好新建个目录解包到其下。

linuxrc方式:
如本目录下的ramdisk8M.image.gz为gzip压缩文件(来源于zed板),gzip -d "文件名"解压后为ramdisk8M.image,通过file命令查看该文件,显示信息如下:
ramdisk8M.image: Linux rev 1.0 ext2 filesystem data (mounted or unclean), UUID=e049cc82-2532-4bcd-8fb8-ad81a5f5991f, volume name “ramdisk”
可以通过命令mount ramdisk8M.image ./tmp -o loop将其挂载到同目录下的tmp目录下,则可以查看其内容。
卸载命令为umount tmp

本目录下的initrd.img-4.4.0-21-generic也为gzip压缩文件(来源于Ubuntu16.04桌面版),重命名为*.gz解压后用file命令查看,显示信息如下:
initrd.img-4.4.0-21-generic: ASCII cpio archive (SVR4 with no CRC)

对cpio的理解

cpio:从归档中拷贝进/出文件。类似于tar归档管理器, 不同的是tar可以通过选项j调用bz2,z调用gunzip等压缩软件来归档的时候压缩文件。
cpio支持归档为二进制或其他格式的文件。
选项说明:

-i:从归档展开文件(run in copy-in mode)
-o:创建归档(run in copy-out mode)
-v:verbos,列详细信息,即显示过程,同tar的-v
-d:按需创建目录(--make-directories)
-m:保留以前文件的修改时间(--preserve-modification-time)
一般用法:
创建归档:cpio -o initrd.cpio 文件(目录)列表
解开归档:cpio -idmv initrd.cpio

四、initrd.lz文件制作

initrd.lz实际是cpio格式文件压缩为lzma格式后的文件

1、解包initrd.lz
 
$ mv initrd.lz initrd.lzma
$ lzma -d initrd.lzma    # uncompress lzma file to cpio file
$ mkdir initramfs && cd initramfs/
$ cpio -idmv < ../initrd    # 解包cpio文件
2、打包initrd.lz文件
find . | cpio -H newc -o > test.cpio    格式:ASCII cpio archive (SVR4 with no CRC)
find . | cpio -H tar -o > test.cpio     格式:tar archive
选项说明:
-H format
使用format包格式:
format可以是:
'bin'    The obsolete binary format. (2147483647 bytes)
'odc'    The old (POSIX.1) portable format. (8589934591 bytes)
‘newc’   The new (SVR4) portable format, which supports file systems having more than 65536 i-nodes. (4294967295 bytes)

注:
解包和打包initrd.lz文件实际是两个过程,即打包成cpio格式文件和压缩成lzma格式文件,因为单独操作两个步骤存在中间修改文件名的步骤,所以制作的initrd.lz不能被内核引导,因此打包解包的命令应该合并成一条命令完成,具体如下:
1、解包initrd.lz
$ xz -dc initrd.lz | cpio -id
2、打包initrd.lz
$ find . | cpio -H newc -o | xz -9 --format=lzma > ../initrd.lz
只有这样制作的initrd.lz才能被内核引导

ubuntu含有微码的新initrd.lz文件操作,微码的知识见本文附录1
新版ubuntu18.04的initrd使用了将几个不同类型的压缩包串成一个文件的形式,
新型initrd解压及打包
1、先安装binwalk查看器查看initrd, apt install binwalk
$ binwalk initrd.img # 输出如下:等待输出完成

DECIMAL       HEX           DESCRIPTION
-------------------------------------------------------------------------------------------------------
0             0x0           ASCII cpio archive (SVR4 with no CRC), file name: "." 
112           0x70          ASCII cpio archive (SVR4 with no CRC), file name: "kernel" 
232           0xE8          ASCII cpio archive (SVR4 with no CRC), file name: "kernel/x86" 
356           0x164         ASCII cpio archive (SVR4 with no CRC), file name: "kernel/x86/microcode" 
488           0x1E8         ASCII cpio archive (SVR4 with no CRC), file name: "kernel/x86/microcode/AuthenticAMD.bin" 
28072         0x6DA8        ASCII cpio archive (SVR4 with no CRC), file name: "TRAILER!!!" 
28672         0x7000        ASCII cpio archive (SVR4 with no CRC), file name: "kernel" 
28792         0x7078        ASCII cpio archive (SVR4 with no CRC), file name: "kernel/x86" 
28916         0x70F4        ASCII cpio archive (SVR4 with no CRC), file name: "kernel/x86/microcode" 
29048         0x7178        ASCII cpio archive (SVR4 with no CRC), file name: "kernel/x86/microcode/.enuineIntel.align.0123456789abc" 
29212         0x721C        ASCII cpio archive (SVR4 with no CRC), file name: "kernel/x86/microcode/GenuineIntel.bin" 
2440880       0x253EB0      ASCII cpio archive (SVR4 with no CRC), file name: "TRAILER!!!" 
2441216       0x254000      gzip compressed data, from Unix, last modified: Tue Aug  6 03:28:43 2019
13315927      0xCB2F57      gzip compressed data, has CRC, extra field, from NTFS filesystem (NT), last modified: Mon Jul 20 16:46:11 1992

可以看到:
第一段数据是格式为cpio的amd微码, 结束符为 “TRAILER!!!”
使用命令:$ cpio -idmv < initrd # 解压出第一段amd微码部分
第二段数据是格式为cpio的intel微码,该段的开头为偏移28672字节 ,因此使用如下命令解压

$ dd if=initrd bs=28672 skip=1 of=second
$ cpio -idm < ../second

第三段数据是格式为gzip的initrd,开头为2441216 偏移
解压:gzip只能分开操作,不能用管道
(1)解出第一段微码–amd微码
(3)解除initrd文件系统微码
dd if=initrd bs=1 skip=2441216 of=middle.gz #解压出后续gzip部分
gzip -d middle.gz 解压gzip部分

五、ramdisk

ramdisk 驱动是用内存作为块设备的一种方式。因使用buffer cache机制使得它可以动态增长。在目标系统中的节点是/dev/ram0~15.(默认16个)
初始化文件系统initrd机制需要该特性。
可以通过内核配置打开该选项:

Device Drivers  --->
  [*] Block devices  --->
    <*>   RAM block device support
    (16)    Default number of RAM disks (NEW)
    (65536)  Default RAM disk size (kbytes) (NEW) /* 64MB */
对应.config中的字段为: 
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_COUNT=16
CONFIG_BLK_DEV_RAM_SIZE=65536

引导参数: ramdisk_size= 与上述配置中的大小选项作用相同,单位也是kbytes
[4] bootparam(7)
[5] 内核文档ramdisk

5.1 ramdisk制作

# -b 指定块的数量(默认块大小1024 bytes,可以通过-B选项指定, 一起决定生成的ramdisk.img的大小)
# -N: 指定节点数量(默认3360) 
# 文件系统参数可以通过 dumpe2fs ramdisk.img 或 tune2fs -l ramdisk.img 查看, 或使用 tune2fs 修改
$ genext2fs -b 163840 -d safemode/ ramdisk.img
$ genext2fs -b 163840 -N 204800 -d safemode/ ramdisk.img
$ gzip -v9 -c ramdisk.img  # v:verbose,9:最高压缩等级(也是默认值),会覆盖ramdisk.img文件,生成ramdisk.img.gz
$ gzip -v9 -c ramdisk.img > ramdisk.img.gz # 不会覆盖

注:
-b 163840中数字是以K为单位的字节数,为160M,409600表示400M
注意该值是内存盘大小,以前以为稍微比待制作的目录大小大一点就行,结果导致空间不足
该值实际应该是根文件系统在内存中占用的大小,并不是该值越大,压缩后的ramdisk.img.gz越大
-N 204800 是指生成的文件系统中节点数为204800,即可以有这么多文件个数,因为文件系统中若节点数不够的话,虽然存储空间够,也会报无存储空间的错误,所以要关注此选项
该值太大了的话因为节点数据也会占用存储空间,所以实际存储空间会变小,如genext2fs -b 163840 -N 163840 参数的话,实际存储空间只有140M,理解了节点数代表可存放的文件数的概念之后可以合适的选用此值,一般20480,两万个文件应该够了

2、genext2fs源码, 从这里 可下载最新版genext2fs-1.4.1.tar.gz

典型的uboot引导ramdisk文件系统的例子如下:

=> setenv bootargs 'console=ttymxc2,115200 init=/init initrd=0x14000000,0x3C00000  ramdisk_size=0x3C00000 rw  root=/dev/ram0 video=mxcfb0:dev=ldb,if=RGB666,fbpix=RGB24,ldb=sin0 video=mxcfb1:off  video=mxcfb2:off fbmem=40M  fb0base=0x27b00000 ubootversion=5caf37c318e299c788b2f96183278c4fcac64275-'
=> setenv bootcmd 'mmc dev 2;mmc read 0x10800000 0x37000 0x4000;mmc read 0x13000000 0x3B800 0x400;mmc read 0x14000000 0x3C000 0x1E000;bootm 0x10800000 - 0x13000000'

initrd和ramdisk的区别与联系
/dev/initrd 是一个设备号为1:250的只读块设备,是initrd机制的一部分。
使用 /dev/initrd 的内核选项为 CONFIG_BLK_DEV_INITRD=y
内核配置如下:

General setup  --->
  [*] Initial RAM filesystem and RAM disk (initramfs/initrd) support
  ()    Initramfs source file(s)
  [*]   Support initial ramdisk/ramfs compressed using gzip
  [ ]   Support initial ramdisk/ramfs compressed using LZMA

initrd和ramdisk没有直接的关系,打开initrd选项可以不开ramdisk选项,内核亦可以正常加载initrd为内存根文件系统,底层使用的是buffer cache,而不是内存块设备驱动。
Documentation/admin-guide/initrd.rst

附录1:微码的概念

有人发现微处理器存在预测执行和预测执行方面的安全隐患,或使得未经授权的人通过旁路攻击来读取内存,它就是‘幽灵’(Spectre)漏洞。
一名本地攻击者可借此获取敏感信息,包括内核内存(公告编号:CVE-2017-5715)。
微码下载
微码概念

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值