mkinitcpio (简体中文)

From ArchWiki
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
翻译状态:本文是 Mkinitcpio翻译。上次翻译日期:2018-12-29。如果英文版本有所更改,则您可以帮助同步翻译。

mkinitcpio 是一个创建 initramfs 的 bash 脚本。

初始内存盘本质上是一个很小的运行环境(早期用户空间),用于加载一些核心模块,并在 init 接管启动过程之前做必要的准备。有了这个环境,才能支持加密根文件系统、RAID上的根文件系统等高级功能。mkinicpio 支持自定义的钩子扩展、运行时自动检测以及其他功能。

从前,内核在启动过程早期(挂载根目录和启动init之前)处理一切硬件的检测和初始化。然而,但随着技术的演进,这种做法变得十分繁琐。

如今,根文件系统可能位于各种硬件上,如SCSI设备、SATA设备、U盘,而这些硬件受控于形形色色的厂家所提供的五花八门的驱动之下。另外,根系统可以加密、压缩,可存放在RAID阵列中,或者一个逻辑卷组上。为了简化这复杂的过程,可以把管理权转入一个用户空间:初始化内存盘。

另见:/dev/brain0 » Blog Archive » Early Userspace in Arch Linux

安装

安装 软件包 mkinitcpio。这个软件包是 linux 软件包的依赖,应该已经自动安装了。高级用户可以从mkinitcpio-gitAUR 获取 mkinitcpio 的最新开发版本.

注意: 若要使用git开发版本,强烈建议同时加入arch-projectsarch-projects 邮件列表!

创建和启用镜像

每次升级内核,mkinitcpio都会默认创建两个内存盘镜像:默认镜像/boot/initramfs-linux.imgfallback镜像/boot/initramfs-linux-fallback.imgfallback镜像和默认镜像只有一个区别,就是创建时跳过了autodetect钩子扩展,因而它包含更多的内核模块。autodetect扩展会探测硬件信息,针对硬件向镜像添加需要的模块,因此缩小了镜像。

在创建初始化内存盘镜像时,可以使用很多不同配置。创建完成后,在启动引导器配置文件中添加启动项目,即可使用新镜像。更改mkinitcpio配置后,需要手动重新生成镜像。以Arch默认的内核linux为例:

# mkinitcpio -p linux

-p选项定义了要使用的预配置(preset);多数内核软件包都会提供一套mkinitcpio预配置文件,放在/etc/mkinitcpio.d目录(比如,linux内核是/etc/mkinitcpio.d/linux.preset)。预配置文件中包含内存盘镜像的基本配置。

警告: 每次升级内核,系统都会自动使用preset文件重新生成内存盘镜像。不要轻易编辑这些文件。

手动生成自定义的 initcpio

使用其他配置文件创建镜像, 例如下面命令会根据/etc/mkinitcpio-custom.conf配置的内容生成镜像/boot/linux-custom.img.

# mkinitcpio -c /etc/mkinitcpio-custom.conf -g /boot/linux-custom.img

为其他不是当前正在运行的内核创建镜像,添加内核版本到命令行, 可以在/usr/lib/modules目录查看支持的内核版本.

 # mkinitcpio -g /boot/linux-custom2.img -k 3.3.0-ARCH

配置

mkinitcpio的主配置文件是/etc/mkinitcpio.conf。此外,内核软件包的预配置文件位于/etc/mkinitcpio.d(例如:/etc/mkinitcpio.d/linux.preset)。

警告: lvm2mdadmencrypt支持默认是关闭的。如果需要使用这些钩子扩展,请仔细阅读本章。

用户可以编辑配置文件中的六个变量:

MODULES
在钩子扩展运行前需要加载的内核模块。
BINARIES
内存盘镜像中包含的额外的二进制文件。
FILES
内存盘镜像中包含的其他文件。
HOOKS
要执行的钩子扩展。
COMPRESSION
内存盘镜像的压缩方式。
COMPRESSION_OPTIONS
传递给压缩工具的额外参数,不建议使用,可以自动根据压缩算法传递需要的参数(例如对 xz 传递 --check=crc32 to xz), 手动设置了错误的参数可能导致系统无法启动。

模块(MODULES)

MODULES数组指定了启动过程最开始时就要加载的模块。

如果模块名称前加上一个“?”问号,那么即使系统无法找到该模块也不会报错。对于自己编译的内核,其中内置了某些模块,该功能可能有用。

  • 如果使用reiser4,该模块必须放入MODULES数组。
  • 如果运行 mkinitcpio 时未加载某些文件系统的模块,而系统启动时又必须使用这些文件系统——比如,LUKS加密密匙文件在ext2分区上,而使用mkinitcpio时系统并未挂载任何ext2分区——那么该文件系统的模块也必须放在MODULES数组。详情参见 此文。}}

如果挂载root分区时需要上述任一模块,请将其加入/etc/mkinitcpio.conf,以避免内核崩溃。

若使用多个拥有相同节点名、但内核模块不同的硬盘控制器(如两个SCSI/SATA或两个IDE控制器),应该使用永久性块设备名称。否则,系统无法确定根目录位置。

附加文件(BINARIES、FILES)

这两个选项允许用户添加任何文件到镜像中。BINARIESFILES数组指定了要加入内存盘镜像的文件,可以覆盖钩子扩展提供的文件。BINARIES中的二进制文件会自动放入一个标准的PATH路径,而且会自动加入可执行文件依赖的函数库。FILES中的文件则不进行上述处理,直接原样放入镜像。例如:

FILES=(/etc/modprobe.d/modprobe.conf)
BINARIES=(kexec)

配置支持多个选项,用空格隔开。

钩子(HOOKS)

HOOKS设置是此文件中最重要的设置。Hooks 是一系列的小脚本,描述那些东西需要加入到镜像里面。有些钩子还包含了运行组建,在启动时执行特定动作,例如启动服务、构建存储栈等。Hooks 按照配置文件中HOOKS项中给出的顺序执行。

默认的HOOKS可以满足大部分简单的单硬盘系统。对于LVM, mdadmLUKS 等复杂根分区系统,请查看相应的 Wiki 页面。

PS/2 键盘用户 : 要在初始化早期阶段使用键盘,请把 keyboard 钩子加入 HOOKS. 个别的键盘无法自动检测到 i8042 控制器,出现 i8042: PNP: No PS/2 controller found. Probing ports directly 报错信息,导致无法使用键盘,请把 atkbd 加入 MODULES.

编译钩子

编译钩子位于/usr/lib/initcpio/install。这些文件会在运行 mkinitcpio 时被包含进脚本,应该提供如下两个函数:buildhelp.build 函数描述了需要加入镜像的模块、文件和二进制文件。mkinitcpio(8) 中的一个 API 会使用这些信息。help 函数在钩子执行后输出描述信息。

列出可用的钩子列表:

$ mkinitcpio -L

使用 mkinitcpio 的 -H/--hookhelp 选项输出对于某一钩子的帮助。例如:

$ mkinitcpio -H udev

运行时钩子

运行时钩子可以在/usr/lib/initcpio/hooks中找到. 对于任何运行时挂钩,都应该始终有一个同名的构建钩子,它会调用add_runscript将运行时挂钩添加到映像中。 这些文件是由早期用户空间中的busybox ash shell提供的。除清理钩子以外,它们始终将按照HOOKS设置中列出的顺序运行。 运行时钩子可能包含以下功能:

run_earlyhook: 一旦挂载了API文件系统并且已经解析了内核命令行,便会运行这个功能。 通常,从此处启动早期引导过程所需的其他守护程序(例如udev)。

run_hook: 这个功能在早期挂接后不久便运行。 这是最常见的挂钩点,应在此处进行诸如堆叠块设备的组装之类的操作。

run_latehook: 此名称的功能在安装根设备后运行。 应当谨慎地将其用于根设备的进一步设置,或用于挂载其他文件系统,例如/usr

run_cleanuphook: 该名称的功能将尽可能晚地运行,并且以与配置文件的HOOKS设置中列出功能的相反顺序来运行。 这些钩子应该用于所有的最终清理操作,例如关闭由早期钩子启动的所有守护程序。

常用钩子

下面是一个常见钩子和功能的表格。注意这个表格是不完整的,因为一些包会提供一些自定义的钩子。

当前钩子
钩子 安装 运行
base 设置初始目录并安装基本工具和库,一般都需要将其加为第一个钩子。 --
systemd 在 initramfs 中安装一个基本的 systemd,会替代掉 'base', 'usr', 'udev' 钩子。其它钩子目前还需要调整。 如果使用了其它 systemd 钩子("sd-*"),这个钩子是 必须的。 从 systemd 207 开始,这个钩子和 lvm2 一起使用的时候可能会引起 boot 损坏,请使用 sd-lvm2 钩子替代 lvm2。同属,可以考虑保留 'base' 钩子(放在 systemd 钩子前) 以确保 initramfs 中包含应急 shell. systemd 217 此钩子还会安装从 hibernation 恢复的服务和执行程序。 --
btrfs 加入启用 Btrfs 根目录和 subvolumes 需要的模块. 如果没有 udev 钩子,会运行 "btrfs device scan" 生成多设备 btrfs 根文件系统。此钩子需要 btrfs-progs
udev 在镜像中加入 udevd, udevadm 和一小部分 udev 规则。 启动 udev 守护进程并处理内核的 uevents 事件,创建设备节点。简化了启动过程,用户不需要显示定义所需的模块,所以推荐使用。
autodetect 通过生成模块白名单缩减 initramfs 的大小,白名单中仅包含 sysfs 中扫描到的模块。请确认加入的模块正确,没有少加模块。此钩子必须在其它子系统钩子前运行,以利于自动检测的优势。'autodetect' 前的模块会全部加入。 --
modconf /etc/modprobe.d/usr/lib/modprobe.d包含 modprobe 配置文件 --
block Adds all block device modules, formerly separately provided by fw, mmc, pata, sata, scsi , usb and virtio hooks. --
pcmcia 添加 PCMCIA 设备需要的模块。需要同时安装 pcmciautils[损坏的链接:package not found] --
net 添加网络设备需要的模块。PCMCIA 网络设备请同时添加 pcmcia 钩子。 处理基于 NFS 的 root 文件系统。
dmraid Provides support for fakeRAID root devices. You must have dmraid installed to use this. Note that it is preferred to use mdadm with the mdadm_udev hook with fakeRAID if your controller supports it. Locates and assembles fakeRAID block devices using dmraid.
filesystems 加入需要的文件系统模块,只要没有在 MODULES 指定文件系统,就必须使用此钩子。 --
lvm2 添加设备映射内核模块和 lvm 工具。需要安装 lvm2 软件包。 启用所有 LVM2 卷组,如果 root 位于 LVM 这是必须的。
sd-lvm2 Use this hook instead of lvm2 when using the systemd hook Enables all LVM2 volume groups. This is necessary if you have your root file system on LVM.
mdadm /etc/mdadm.conf 读取或启动时自动检测磁盘阵列。请优先使用下面的 mdadm_udev 钩子。 mdassemble 定位并组合软 RAID 块设备。
mdadm_udev 通过 udev 组合磁盘阵列。建议在二进制中包含 mdmon 并加入shutdown 钩子以避免重启时不必要的 raid 重建。 使用 udevmdadm 组合软 RAID 块设备。
encrypt 添加 dm-crypt 内核模块和 cryptsetup 工具。需要安装 cryptsetup 软件包。 检查并解密 root 分区。详情参见 #Runtime customization[损坏的链接:无效的章节]
resume -- Tries to resume from the "suspend to disk" state. Works with both swsusp and TuxOnIce[archived page]. See Hibernation for further configuration. Intended to work along with the base hook, the systemd hook provides its own resume mechanism.
keyboard Adds the necessary modules for keyboard devices. Use this if you have an USB keyboard and need it in early userspace (either for entering encryption passphrases or for use in an interactive shell). As a side effect, modules for some non-keyboard input devices might be added to, but this should not be relied on. --
keymap Adds the specified keymap(s) from /etc/vconsole.conf to the initramfs. Loads the specified keymap(s) from /etc/vconsole.conf during early userspace.
consolefont Adds the specified console font from /etc/vconsole.conf to the initramfs. Loads the specified console font from /etc/vconsole.conf during early userspace.
sd-vconsole Adds the keymap(s) and console font specified in /etc/vconsole.conf to the systemd-based initramfs. Loads the specified keymap(s) and console font during early userspace.
sd-encrypt This hook allows for an encrypted root device with systemd initramfs. See the man page of systemd-cryptsetup-generator(8) for available kernel command line options. Alternatively, if the file /etc/crypttab.initramfs exists, it will be added to the initramfs as /etc/crypttab. See the crypttab(5) manpage for more information on crypttab syntax. --
fsck 添加 fsck 程序和文件系统专用工具,如果添加在 autodetect 钩子后面,仅会添加根文件系统需要的工具。强烈推荐所用用户使用, /usr 单独分区的用户必须使用此钩子。 对根分区 (和单独分区的 /usr) 执行 fsck。The use of this hook requires the rw parameter to be set on the kernel commandline (discussion).
shutdown 增加关机 initramfs 支持,从 mkinitcpio 0.16 开始,已经 不再需要 卸载并关闭设备。
usr 加入对单独 /usr 分区的支持 在 root 挂载后挂载 /usr 分区.

编写钩子扩展

一个 initcpio 钩子仅仅是另一段 shell 脚本,它包含了必要的信息指引 mkinitcpio 哪一个可执行文件应该被加载,以及以什么参数被加载。它在某些罕见情况下很有用,比如一些文件需要在用户空间的早期或晚期被加载,而这又没有被其他已经安装的脚本提供。

首先创建实际的脚本:

/usr/lib/initcpio/install/hook
 #!/bin/bash
 
 build() {
     add_binary /bin/bash  #/bin/bash is given as an example, you can type your desired executable here
     add_runscript  # will determine the name of the script, and add appropriately from /usr/lib/initcpio/hooks
 }
 
 help() {
     cat <<HELPEOF
     Line used as help information #Typing mkinitcpio -H hook will display this information
 HELPEOF
 }

然后创建这个实际的钩子:

/usr/lib/initcpio/hooks/hook
 run_hook ()
 {
     msg -n ":: This is an example hook"
     bash #your executable example
     msg "done."
 }
注意: 没有必要为这两个脚本设定可执行权限,也不建议这样做。

现在编辑 /etc/mkinitcpio.conf 来包含你的自定义钩子。你也可在 /etc/rc.sysinit 包含脚本来在用户空间的后期加载。

压缩方式(COMPRESSION)

内核支持几种initramfs的压缩方式 - gzip, bzip2, lzma, xz (也被称为 lzma2), lzo 和 lz4。对于大多数使用的情况,gzip, lzop, lz4 提供了基本的压缩大小和解压缩速度的平衡。

系统中的 mkinitcpio.conf 已经注释掉了各种 COMPRESSION 选项,要使用某个压缩方式,请取消前面的注释。不指定 COMPRESSION 选项会使用 gzip 压缩的 initramfs 文件。想要创建一个未压缩的镜像,在配置中指定COMPRESSION=cat 或者在命令行使用 -z cat.

请按照选择的压缩方式安装需要的工具。

压缩选项(COMPRESSION_OPTIONS)

还有一些额外的标准可以传递到 COMPRESSION 指定的程序,例如:

COMPRESSION_OPTIONS='-9'

通常这些是不需要的,因为 mkinitcpio 会确保任何支持的压缩方法有必要的标志来产生一个可以工作的镜像。

运行时配置

运行时配置选项可以通过内核命令行传递到 init 以及某些钩子。内核命令行参数通常是由启动引导器提供。参见 Arch 启动过程Kernel parameters以获取更多信息。

从基本钩子启动

root
这是内核命令行中指定的最重要的参数,因为它决定了哪一个设备会被挂载为你的正确的 root 设备。mkinitcpio 可以灵活的允许不同的格式,例如:
root=/dev/sda1                                                # /dev 节点
root=LABEL=CorsairF80                                         # 卷标
root=UUID=ea1c4959-406c-45d0-a144-912f4e86b207                # UUID
root=PARTUUID=14420948-2cea-4de7-b042-40f67c618660            # GPT partition UUID
注意: 下面的选项改变了 init 在 initramfs 环境中的默认行为。参见 /lib/initcpio/init 获取更多信息。
break
如果break 或者 break=premount 被指定,init 暂停启动过程(在加载钩子之后,但是在挂载根文件系统之前) 然后启动一个交互的 shell,可以用来解决问题。这个 shell 可以在root被指定的break=postmount挂载之后启动。正常的启动过程可以在退出这个 shell 之后继续。
disablehooks
在运行时可以通过添加 disablehooks=hook1{,hook2,...} 禁用某些钩子。例如
disablehooks=resume
earlymodules
改变模块加载的顺序。可以通过 earlymodules=mod1{,mod2,...} 指定一些模块提前加载。 (例如,这可以用来确保多个网络接口的正确顺序。)
rootdelay
通过附加 rootdelay 在挂载根文件系统之前暂停十秒。(它的用途是,例如,从USB硬盘启动时会花更长时间来初始化。)

参见: GRUB 和 init 调试

使用 RAID 磁盘阵列

首先,在 /etc/mkinitcpio.conf 文件中把 mdadm_udevmdadm 钩子加入到 HOOKS 数组中,以及将任何需要的 RAID 模块(raid456, ext4)添加到 MODULES 数组中。

内核参数: 使用 mdadm 钩子,你不再需要在 kernel parameters 参数中配置你的 RAID 磁盘阵列。在启动过程中(init phase),mdadm 钩子会或者使用你的 /etc/mdadm.conf 文件或者自动探测磁盘阵列。

也可通过使用 mdadm_udev 钩子,使用 udev 组装(assemble)。上游倾向于使用这个组装方法。/etc/mdadm.conf 仍然会被读取,目的是命名可能存在的已经组装的设备。

使用 net

警告: 尚未支持 NFSv4。

需要的包:

net 需要 mkinitcpio-nfs-utils 包,在官方源中就有。

内核参数:

内核文档包含最新的信息和清晰的说明。

ip=

This parameter tells the kernel how to configure IP addresses of devices and also how to set up the IP routing table. It can take up to nine arguments separated by colons: ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>:<dns0-ip>:<dns1-ip>.

If this parameter is missing from the kernel command line, all fields are assumed to be empty, and the defaults mentioned in the kernel documentation apply. In general this means that the kernel tries to configure everything using autoconfiguration.

The <autoconf> parameter can appear alone as the value to the 'ip' parameter (without all the ':' characters before). If the value is "ip=off" or "ip=none", no autoconfiguration will take place, otherwise autoconfiguration will take place. The most common way to use this is "ip=dhcp".

例子:

 ip=127.0.0.1:::::lo:none  --> Enable the loopback interface.
 ip=192.168.1.1:::::eth2:none --> Enable static eth2 interface.
 ip=:::::eth0:dhcp --> Enable dhcp protocol for eth0 configuration.
注意: Make sure to use kernel device names (e.g. eth0) for the <device> parameter, and not udev[损坏的链接:无效的章节] ones (e.g. enp2s0) as those will not work.

BOOTIF= 使用多个网卡的时候,此参数可以指定要使用网卡的 Mac 地址。在接口号可能变化或与 IPAPPEND 2 、IPAPPEND 3 选项共用时比较有用。默认使用 eth0.

示例:

BOOTIF=01-A1-B2-C3-D4-E5-F6  # Note the prepended "01-" and capital letters.

nfsroot=

如果命令行中没有给出 nfsroot 参数,那么默认的 /tftpboot/%s 会被使用。

 nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>]

使用 lvm

如果你的根设备是在LVM 上,你必须添加 lvm2 钩子。请阅读 这里[损坏的链接:无效的章节].

使用加密根目录

如果使用 加密 root,请参考 Dm-crypt/System configuration#mkinitcpio

/usr 放到单独分区

如果将 /usr 放在单独分区,必须满足:

  • 添加 fsck 钩子,在/etc/fstab中将/usrpassno设置为0。这个对其他用户是推荐选项,而对 /usr 单独分区用户是硬性要求。不添加这个钩子,没有此选项,系统不好对/usr进行磁盘检查。
  • 从 mkinitcpio 0.9.0 开始: 添加上面的钩子和 usr 钩子,它会在 root 挂载后挂载 /usr 分区。

疑难解答

解压缩镜像

如果你对 initrd 镜像的内容感到好奇,你可以解压缩它然后看看里面的文件。

initrd 镜像是一个 SVR4 CPIO 压缩包,通过 findbsdcpio 命令生成,可选可被内核理解的压缩方式参考上面的压缩部分。

mkinitcpio 包含了一个叫做 lsinitcpio 的工具,可以列出和解压缩出 initramfs 镜像的内容。

你可以用下面的命令列出镜像中的文件:

$ lsinitcpio /boot/initramfs-linux.img

然后全部解压缩到当前文件夹:

$ lsinitcpio -x /boot/initramfs-linux.img

你也可以用更对人类友好的方式列出镜像中的重要的部分:

$ lsinitcpio -a /boot/initramfs-linux.img

重新压缩解压之后修改过的镜像

在按照上面的方法解压了一个镜像并且进行了修改之后,你可以通过以下的方法得到重新压缩它所需要的命令。按照下面的例子修改 /usr/bin/mkinitcpio 中的这一行 (line 531 in mkinitcpio v20-1.):

#MKINITCPIO_PROCESS_PRESET=1 "$0" "${preset_cmd[@]}"
MKINITCPIO_PROCESS_PRESET=1 /usr/bin/bash -x "$0" "${preset_cmd[@]}"

然后用常见的参数运行 mkinitcpio (一般来说是 mkinitcpio -p linux), 大约在最后的20行你会看见类似下面的一些输出:

+ find -mindepth 1 -printf '%P\0'
+ LANG=C
+ bsdcpio -0 -o -H newc --quiet
+ gzip

它们代表你所需要运行的命令,比如像这样:

# find -mindepth 1 -printf '%P\0' | LANG=C bsdcpio -0 -o -H newc --quiet | gzip > /boot/initramfs-linux.img
警告: 建议在运行上面的命令产生一个新的/boot/initramfs-linux.img之前把现有的镜像重命名作为备份,以便情况不妙时有后悔药吃。如果因为失误导致系统无法启动,你需要用fallback镜像或者boot CD启动,然后运行mkinitcpio来overwrite掉之前做的更改,又或者你自己修好它然后重新压缩镜像。

"/dev must be mounted" when it already is

The test used by mkinitcpio to determine if /dev is mounted is to see if /dev/fd/ is there. If everything else looks fine, it can be "created" manually by:

# ln -s /proc/self/fd /dev/

(Obviously, /proc must be mounted as well. mkinitcpio requires that anyway, and that is the next thing it will check.)

Possibly missing firmware for module XXXX

When initramfs are being rebuild after a kernel update, you might get these two warnings:

==> WARNING: Possibly missing firmware for module: aic94xx
==> WARNING: Possibly missing firmware for module: smsmdtv 

These appear to any Arch Linux users, especially those who have not installed these firmware modules. If you do not use hardware which uses these firmwares you can safely ignore this message.

Standard rescue procedures

With an improper initial ram-disk a system often is unbootable. So follow a system rescue procedure like below:

Boot succeeds on one machine and fails on another

mkinitcpio's autodetect hook filters unneeded kernel modules in the primary initramfs scanning /sys and the modules loaded at the time it is run. If you transfer your /boot directory to another machine and the boot sequence fails during early userspace, it may be because the new hardware is not detected due to missing kernel modules.

To fix, first try choosing the fallback[损坏的链接:无效的章节] image from your bootloader, as it is not filtered by autodetect. Once booted, run mkinitcpio on the new machine to rebuild the primary image with the correct modules. If the fallback image fails, try booting into an Arch Linux live CD/USB, chroot into the installation, and run mkinitcpio on the new machine. As a last resort, try manually[损坏的链接:无效的章节] adding modules to the initramfs.

参考资料