Linux Boot Process

2022年8月2日 317点热度 0人点赞 0条评论

引导过程是指从用户打开主机 (物理机或虚拟机) 电源,到操作系统完全加载这段时间所发生的事情。

它主要包括三个阶段:由硬件 (物理硬件或虚拟硬件) 执行的初始启动、操作系统初始阶段加载、启动在系统中运行所需服务的机制。本章围绕这三个阶段展开讨论,并会涉及一些系统救援相关的操作。

不需要对引导过程的前两个阶段做过多修改,但熟悉它们,有助于在遇到故障时进行恢复。

第三个阶段由 systemd 管理,可以在此阶段修改系统中默认运行的服务。在前几章中,已经介绍过很多 systemd 相关的知识,本章会对它们进行一些回顾。

进入第一阶段的讨论。

23.1 BIOS、UEFI 引导过程介绍

计算机包含硬件嵌入式软件控制器,也称为固件 (firmware),通过固件可以管理底层硬件。固件首先识别系统中可用的硬件、以及硬件启用了哪些功能 (如 pre-boot network execution,PXE)。

x86 架构的个人电脑 (PC) 的嵌入式固件称为 BIOS,是基本输入输出系统 (Basic Input and Output System) 的缩写。

Linux 系统的 BIOS 启动过程有以下步骤:

  1. 主机上电并加载 BIOS 固件;
  2. 固件初始化设备,如键盘、鼠标、存储等外设;
  3. 固件读取配置,包括引导顺序、指定继续引导过程的存储设备;
  4. 选择存储设备后,BIOS 在其上加载 MBR (Master Boot Record),运行操作系统加载器。在 Linux 中,操作系统加载器称为 GRUB (Grand Unified Bootloader);
  5. GRUB 加载配置以及在配置中指定的操作系统内核和 initramfs。内核存储在 vmlinuz 开头的文件中,初始引导镜像存储在 initramfs 开头的文件中。GRUB 配置文件、vmlinuz、initramfs 都存储在 /boot/ 分区中;
  6. 初始引导镜像加载系统中的第一个进程 systemd
  7. systemd 加载系统的其它服务。

要实现此过程,磁盘上必须有一个 MBR 分区表,并且分配给 /boot/ 的分区必须标记为 “bootable”

MBR 分区表格式有限,只允许四个主分区,并使用扩展分区等扩展功能来弥补这一限制。除非必要,否则不建议使用 MBR 分区。

UEFI (Unified Extensible Firmware Interface,统一可扩展固件接口) 的引导过程与 BIOS 的引导过程非常相似,主要区别是 UEFI 可以直接访问和读取磁盘分区。过程如下:

  1. 机器上电并加载 UEFI 固件;
  2. 固件初始化设备,如键盘、鼠标、存储等外设;
  3. 固件读取配置,指定继续引导过程的存储设备和可引导分区,不再需要 MBR 引导;
  4. 选择存储设备后,会从 GUID 分区表 (GUID Partition Table,简称 GPT) 读取分区。访问 VFAT 格式的第一个分区。加载并运行 EFI 引导器。EFI 引导器位于 /boot/efi/ 目录下。EFI 引导器加载 GRUB。
  5. GRUB 加载操作系统内核,内核存储在 vmlinuz 开头的文件中,初始引导镜像存储在 initramfs 开头的文件中。GRUB 配置、vmlinuz、initramfs 文件都存储在 /boot/ 分区中;
  6. 初始引导镜像加载系统中的第一个进程 systemd
  7. systemd 加载系统的其余服务。

与 BIOS 相比,UEFI 有几个优势,能够实现更完整的 pre-boot 环境和其他功能,如安全引导和 GPT 分区,GPT 分区可以超越 MBR 分区的 2TB 限制。

安装器负责创建引导,如果需要,还将创建 UEFI 分区和二进制文件。

通过 BIOS 或 UEFI,可以选择操作系统从哪个存储设备加载并进入下一个阶段。

下一节开始讨论第二个阶段。

23.2 GRUB、bootloader、initramfs

pre-boot 执行完成后,系统将运行 GRUB bootloader。

GRUB 的任务是加载操作系统内核,向内核传递参数和选项,并加载 initramfs。

GRUB 可以通过 grub2-install 命令安装。需要确定用作设备引导的磁盘,在本例中是 /dev/nvme0n1

[root@rocky08-host ~]# grub2-install /dev/nvme0n1Installing for i386-pc platform.Installation finished. No error reported.

grub-install 命令后边指定用于引导系统的磁盘,也就是在 BIOS 或者 UEFI 中配置的引导磁盘。这条命令会手动重建或修复损坏的系统引导。

GRUB 文件存放在 /boot/grub2/ 目录下。主配置文件为 /boot/grub2/grub.cfg。查看文件,会看到以下开头:

[root@rocky08-host ~]# head -n 6 /boot/grub2/grub.cfg## DO NOT EDIT THIS FILE## It is automatically generated by grub2-mkconfig using templates# from /etc/grub.d and settings from /etc/default/grub#

文件是自动生成的,不要编辑这个文件。如果必须修改它,有两种方法:

  • 第一种方法是,按照 grub.cfg 文件中的提示,编辑 /etc/default/grub 文件和 /etc/grub.d/ 目录中的内容,然后通过 grub2-mkconfig 命令重新生成 GRUB 配置。
  • 第二种方法是,使用 grubby 命令。

在 Linux 中,当更新内核时,不会对现有内核进行升级,而是安装新的内核版本,保留旧的内核版本,并在 GRUB 中添加条目。这样方便在有需要时回退内核版本。在安装新内核的过程中,会为新内核创建一个新的更新后的 initramfs。

可以使用 grubby 命令查看当前的内核配置。--default-kernel 选项显示默认加载的内核文件:

[root@rocky08-host ~]# grubby --default-kernel/boot/vmlinuz-4.18.0-372.9.1.el8.x86_64

--default-title 选项可以显示引导时使用的名称:

[root@rocky08-host ~]# grubby --default-titleRocky Linux (4.18.0-372.9.1.el8.x86_64) 8.6 (Green Obsidian)

使用 --info 选项,可以查看内核的更多信息:

[root@rocky08-host ~]# grubby --info /boot/vmlinuz-4.18.0-372.9.1.el8.x86_64index=0kernel="/boot/vmlinuz-4.18.0-372.9.1.el8.x86_64"args="ro crashkernel=auto resume=/dev/mapper/rl-swap rd.lvm.lv=rl/root rd.lvm.lv=rl/swap biosdevname=0 net.ifnames=0 rhgb quiet $tuned_params"root="/dev/mapper/rl-root"initrd="/boot/initramfs-4.18.0-372.9.1.el8.x86_64.img $tuned_initrd"title="Rocky Linux (4.18.0-372.9.1.el8.x86_64) 8.6 (Green Obsidian)"id="259d4d4e70e8414fb7dc745fbd14737e-4.18.0-372.9.1.el8.x86_64"

可以看到传递给 GRUB 的选项:

  • index:显示指定内核的索引号;
  • kernel:包含将加载以运行操作系统核心的内核文件;
  • root:分配给 / 目录并挂载的分区或逻辑卷;
  • initrd:包含 RAM 磁盘的文件,用于执行引导过程的初始部分;
  • title:在启动过程中向用户显示的描述性信息;
  • id:引导项的标识符。

使用 grubby 命令查看默认内核的信息:

[root@rocky08-host ~]# grubby --info $(grubby --default-kernel)

可以对内核的启动参数进行修改,例如删除 quietrhbg 参数,使引导过程更详细:

[root@rocky08-host ~]# grubby --remove-args="rhgb quiet" --update-kernel=/boot/vmlinuz-4.18.0-372.9.1.el8.x86_64[root@rocky08-host ~]# grubby --info $(grubby --default-kernel)index=0kernel="/boot/vmlinuz-4.18.0-372.9.1.el8.x86_64"args="ro crashkernel=auto resume=/dev/mapper/rl-swap rd.lvm.lv=rl/root rd.lvm.lv=rl/swap biosdevname=0 net.ifnames=0 $tuned_params"root="/dev/mapper/rl-root"initrd="/boot/initramfs-4.18.0-372.9.1.el8.x86_64.img $tuned_initrd"title="Rocky Linux (4.18.0-372.9.1.el8.x86_64) 8.6 (Green Obsidian)"id="259d4d4e70e8414fb7dc745fbd14737e-4.18.0-372.9.1.el8.x86_64"

通过 systemctl reboot 命令重启主机验证上面的修改:

[root@rocky08-host ~]# systemctl reboot

也可以使用 --args 选项向内核添加参数:

[root@rocky08-host ~]# grubby --args="quiet rhbg" --update-kernel=/boot/vmlinuz-4.18.0-372.9.1.el8.x86_64

要在启动后查看引导信息,可以使用 dmesg 命令。

--info--update-kernel 选项接受 ALL 选项,用来检查或对所有已配置的内核执行操作。例如:

[root@rocky08-host ~]# grubby --info ALLindex=0kernel="/boot/vmlinuz-4.18.0-372.9.1.el8.x86_64"args="ro crashkernel=auto resume=/dev/mapper/rl-swap rd.lvm.lv=rl/root rd.lvm.lv=rl/swap biosdevname=0 net.ifnames=0 $tuned_params quiet rhbg"root="/dev/mapper/rl-root"initrd="/boot/initramfs-4.18.0-372.9.1.el8.x86_64.img $tuned_initrd"title="Rocky Linux (4.18.0-372.9.1.el8.x86_64) 8.6 (Green Obsidian)"id="259d4d4e70e8414fb7dc745fbd14737e-4.18.0-372.9.1.el8.x86_64"index=1kernel="/boot/vmlinuz-4.18.0-348.23.1.el8_5.x86_64"args="ro crashkernel=auto resume=/dev/mapper/rl-swap rd.lvm.lv=rl/root rd.lvm.lv=rl/swap biosdevname=0 net.ifnames=0 rhgb quiet $tuned_params"root="/dev/mapper/rl-root"initrd="/boot/initramfs-4.18.0-348.23.1.el8_5.x86_64.img $tuned_initrd"title="Rocky Linux (4.18.0-348.23.1.el8_5.x86_64) 8.5 (Green Obsidian)"id="259d4d4e70e8414fb7dc745fbd14737e-4.18.0-348.23.1.el8_5.x86_64"index=2kernel="/boot/vmlinuz-4.18.0-348.el8.0.2.x86_64"args="ro crashkernel=auto resume=/dev/mapper/rl-swap rd.lvm.lv=rl/root rd.lvm.lv=rl/swap biosdevname=0 net.ifnames=0 rhgb quiet $tuned_params"root="/dev/mapper/rl-root"initrd="/boot/initramfs-4.18.0-348.el8.0.2.x86_64.img $tuned_initrd"title="Rocky Linux (4.18.0-348.el8.0.2.x86_64) 8.5 (Green Obsidian)"id="259d4d4e70e8414fb7dc745fbd14737e-4.18.0-348.el8.0.2.x86_64"index=3kernel="/boot/vmlinuz-0-rescue-259d4d4e70e8414fb7dc745fbd14737e"args="ro crashkernel=auto resume=/dev/mapper/rl-swap rd.lvm.lv=rl/root rd.lvm.lv=rl/swap biosdevname=0 net.ifnames=0 rhgb quiet"root="/dev/mapper/rl-root"initrd="/boot/initramfs-0-rescue-259d4d4e70e8414fb7dc745fbd14737e.img"title="Rocky Linux (0-rescue-259d4d4e70e8414fb7dc745fbd14737e) 8.5 (Green Obsidian)"id="259d4d4e70e8414fb7dc745fbd14737e-0-rescue"

以上是修改内核参数的方法。下面进入 initramfs 部分。

initramfs 文件包含用于启动系统的最小系统。通过 grubby --info 命令可知当前使用的是 /boot/initramfs-4.18.0-348.23.1.el8_5.x86_64.img。可以通过 dracut 命令重新生成 initramfs 文件:

[root@rocky08-host ~]# dracut --force --verbose...dracut: *** Creating initramfs image file '/boot/initramfs-4.18.0-372.9.1.el8.x86_64.img' done ***...

当 initramfs 文件损坏时,可以使用该命令重建 initramfs。在不同的硬件上通过备份恢复系统时,也可以使用该命令生成正确的存储驱动。

以上是引导过程初期阶段的基本知识,足够用来排查启动问题了。接下来进入第三个阶段的讨论。

23.3 使用 systemd 管理引导顺序

GRUB 加载内核和 initramfs,为系统启动做准备。接着启动系统的第一个进程,也就是 PID 为 1 的进程。这个进程负责加载系统所需的服务。在 Rocky Linux 中,PID 为 1 的进程是 systemd

systemd 与引导顺序相关的前两件事是重启系统和关闭电源。使用 systemctl 命令完成这两项操作:

[root@rocky08-host ~]# systemctl reboot

执行上面的命令,系统会重新启动。可以使用 uptime 命令查看系统已经运行的时间:

[root@rocky08-host ~]# uptime20:03:37 up 0 min,  1 user,  load average: 0.85, 0.30, 0.11

接下来讨论 poweroff。但在这么做之前,要确保有办法启动主机。关闭主机的命令:

[root@rocky08-host ~]# systemctl poweroff

执行完该命令,需要重新开启主机电源。

systemctl halt 命令会挂起系统,但不发送关闭主机电源的信号。该命令很少使用,但知道它没有坏处。

之前的命令可以缩写为 rebootpoweroff。查看 /usr/sbin/poweroff 文件,会发现它是指向 systemctl 的符号链接。

第五章介绍了 systemctl 设置默认 systemd target。然而,可以使用 grubby 命令在引导时通过向内核传递 systemd.unit 参数来覆盖默认配置:

[root@rocky08-host ~]# systemctl get-defaultmulti-user.target[root@rocky08-host ~]# grubby --args="systemd.unit=emergency.target" --update-kernel=$(grubby --default-kernel)[root@rocky08-host ~]# systemctl reboot

系统重启,“systemd.unit=emergency.target” 参数已经被 GRUB 传给了内核,再由内核传给 systemd,后者将忽略默认配置,加载 “emergency target” 所需的服务。

最终系统在紧急模式下启动,等待输入 “root” 密码以登录控制台:

图片
23.1 输入 root 密码,登录紧急模式

在紧急模式下,没有配置网络,也没有运行其他进程。在该模式下,可以在没有其他用户访问系统的情况下对系统进行更改。此外,/ 文件系统是以只读模式挂载的。

如果操作系统的文件系统损坏了,这是在没有任何服务访问的情况下检查文件系统的好方法。用 fsck 命令来检查文件系统:

[root@rocky08-host ~]# fsck /boot

示例中的文件系统是正常的。但如果有需要修复的问题,可以在它上面运行 xfs_repair 命令,因为它是一个 xfs 文件系统。

根文件系统以只读的形式挂载在 / 上,如何对它进行更改呢?可以将 / 文件系统以读写的方式重新挂载:

[root@rocky08-host ~]# mount -o remount -o rw /

现在根文件系统以读写的方式挂载在 / 中。还需要执行 mount /boot 命令:

[root@rocky08-host ~]# mount /boot

挂载 /boot 后,执行一些管理任务,例如删除在 GRUB 中使用的 systemd.unit=emergency.target 参数:

[root@rocky08-host ~]# grubby --remove-args="systemd.unit=emergency.target" --update-kernel=$(grubby --default-kernel)[root@rocky08-host ~]# reboot

系统又能正常启动了。在 Linux 中,这可能不是进入紧急模式的实用方法,但它演示了如何在引导时将参数传递给 systemd

rescue.target 可以加载更多服务,并且也更容易,可以按照上面的步骤尝试进入 rescue.target 模式。

23.4 介入引导过程获得对系统的访问权

在工作交接中,时有遇到遗失 root 密码的系统。

想找回 root 密码,需要重启操作系统。在 BIOS/UEFI 检查完成后,系统会加载 GRUB。在等待选择内核的界面,按向上或向下的箭头终止计数,如图23.2 所示:

图片
图23.2 在 GRUB 菜单手动选择内核

选择第一个条目。

阅读屏幕底部的帮助信息,找到编辑所选项目的说明。按 E 键编辑菜单中选择的行,会输出以下五行内容:

图片
图23.3 GRUB 手动选择内核

带有 load_videoset gfx_payload=keepinsmod gzio 的前三行是 GRUB 的设置选项。最后的两个选项很重要:

  • linux:定义加载的内核以及向内核传递的参数。
  • initrd:定义加载 initrd 的位置以及是否有任何选项。

将光标移动到 linux 行的末尾,添加 rd.break 选项,如图23.4 所示:

图片
图23.4, 编辑 linux 行,加入 rd.break 选项

按 Ctrl + x 键,引导编辑过的行。rd.break 选项在加载 initrd 之前停止引导过程。现在的情况如下:

  • 加载单 shell;
  • 当前挂载在 / 上的文件系统是一个具有基本管理命令的最小文件系统;
  • 目标根文件系统以只读的形式挂载在 /sysroot (而不是挂载在 /);
  • 没有挂载其他文件系统;
  • 没有启动 SELinux。

使用 chroot 命令切换到真正磁盘上的 / 文件系统:

chroot /sysroot

以读写方式重新挂载 / 文件系统:

mount -o remount -o rw /

使用 passwd 命令修改 root 用户的密码:

passwd

root 用户的密码已经更改,并且 /etc/shadow 文件也已更新。但是在没有启用 SELinux 的情况下修改的,在下次引导时可能会导致问题。为了避免这种情况,需要创建 /.autorelabel 隐藏的空文件,系统在下次启动时修复 SELinux 标签。然后重启系统:

touch /.autorelabel

在这种状态下,可能需要强制关闭主机电源,然后再开启。系统重启后,就可以使用 root 用户及其新密码登录操作系统了。

74380Linux Boot Process

这个人很懒,什么都没留下

文章评论