Kexec

来自 ArchWiki

Kexec 是一个系统调用,它允许您从当前运行的内核加载并启动到另一个内核。这对于内核开发者或其他需要非常快速重启而无需等待整个 BIOS 启动过程完成的人员非常有用。请注意,由于设备在使用此方法时没有完全重新初始化,kexec 可能无法正常工作,但这很少见。

安装

安装 kexec-tools 软件包。

使用 kexec 重启

手动

您可以使用以下命令手动调用 kexec

# kexec -l /boot/vmlinuz-linux --initrd=/boot/initramfs-linux.img --reuse-cmdline
# kexec -e
警告: 直接运行 kexec -e 不会卸载活动的文件系统或优雅地终止任何正在运行的服务。

也可以手动加载内核,然后让 systemd 处理服务关闭和 kexec。

# kexec -l /boot/vmlinuz-linux --initrd=/boot/initramfs-linux.img --reuse-cmdline
# systemctl kexec

Systemd

默认情况下,如果使用 systemd-boot 并且之前没有使用 kexec -l 手动加载内核,systemd 将加载默认启动加载器条目中指定的内核。例如,要在系统更新后重启到较新的内核,您可以简单地运行

# systemctl kexec

如果您有多个 initrd 条目(例如用于 Microcode 更新),而当前不支持这些条目,则该命令将拒绝执行。

自定义单元文件

如果默认行为不适合您,或者您希望方便地加载自定义内核,您可以将内核加载包装到一个服务单元中。创建一个新的单元文件 kexec-load@.service,它将加载指定的内核以进行 kexec

/etc/systemd/system/kexec-load@.service
[Unit]
Description=load %i kernel into the current kernel
Documentation=man:kexec(8)
DefaultDependencies=no
Before=shutdown.target umount.target final.target

[Service]
Type=oneshot
ExecStart=/usr/bin/kexec -l /boot/vmlinuz-%i --initrd=/boot/initramfs-%i.img --reuse-cmdline

[Install]
WantedBy=kexec.target

然后为要加载的内核启用服务文件(例如,对于 linux,它将是 kexec-load@linux.service

确保关闭钩子不包含在您的 initramfs 镜像中,方法是从 /etc/mkinitcpio.conf 中的 HOOKS 数组中删除它。如果它在那里,请删除它并重新生成 initramfs

然后执行 kexec

# systemctl kexec

如果您希望为下一次 kexec 加载不同的内核,例如 linux-lts禁用当前内核的服务,并为新内核启用服务。

单独的 /boot 分区

如果 /boot 不在根文件系统上,则上述 systemd 单元文件将失败,因为 systemd 很可能在运行 kexec-load 单元文件之前卸载 /boot。另一种方法是加载一个“钩子”单元文件,该文件在启动时不执行任何操作,并在终止时调用 kexec。通过使此单元文件与 kexec.target 且仅与 kexec.target 冲突,您可以确保新内核尽早加载,并且仅在 systemctl kexec 命令之后加载。这是一个替代的 /etc/systemd/system/kexec-load@.service 文件,它遵循此策略

[Unit]
Description=hook to load vmlinuz-%i kernel upon kexec
Documentation=man:kexec(8)
DefaultDependencies=no
Requires=sysinit.target
After=sysinit.target

[Service]
Type=oneshot
ExecStart=-/usr/bin/true
RemainAfterExit=yes
ExecStop=/usr/bin/kexec -l /boot/vmlinuz-%i --initrd=/boot/initramfs-%i.img --reuse-cmdline

[Install]
WantedBy=basic.target

请注意,Conflicts=shutdown.target 并不是真正需要的,因为它由 sysinit.target 的严格排序隐式保证,而 sysinit.target 本身与 shutdown.target Conflicts=

故障排除

系统在 “kexec_core: Starting new kernel” 后挂起或重启

通用故障排除#启动问题上的故障排除信息可能有助于诊断问题。

在某些情况下,系统挂起可能是 acpi 相关问题,可以像这样动态检查

# cmdline=$(cat /proc/cmdline)
# cmdline="$cmdline acpi_rsdp=$(grep -m1 ^ACPI /sys/firmware/efi/systab | cut -f2- -d=)"
# echo $cmdline
# ls -al /boot/
# kexec -l /boot/vmlinuz-linux-lts --initrd=/boot/initramfs-linux-lts.img --append="$cmdline"
# systemctl kexec

请根据 ls -al /boot/ 的输出来调整 initramfs 镜像和内核的名称。

[1] 中建议将 acpi_rsdp 内核参数添加到 kexec 命令行,并且可能在某些情况下解决问题,而无需通过 acpi=off 完全禁用 ACPI。

无内核模式设置 (Nvidia)

图形驱动程序需要在 kexec 之前卸载,否则下一个内核将无法获得设备的独占控制权。这很难手动实现,因为任何需要独占控制 GPU 的程序(Xorg、显示管理器)都不能运行。以下是一个 systemd 服务 示例,它将在 kexec 之前卸载 KMS 驱动程序,这要求您使用 systemctl kexec

/etc/systemd/system/unmodeset.service
[Unit]
Description=Unload nvidia modesetting modules from kernel
Documentation=man:modprobe(8)
DefaultDependencies=no
After=umount.target
Before=kexec.target

[Service]
Type=oneshot
ExecStart=modprobe -r nvidia_drm

[Install]
WantedBy=kexec.target

之后,启用 unmodeset.service

参见