Kexec
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 条目(例如用于 微码更新),而这些条目当前不受支持,该命令将拒绝执行。
自定义 unit 文件
如果默认行为对您不起作用,或者您希望方便地加载自定义内核,可以将内核加载包装在一个服务 unit 中。创建一个新的 unit 文件 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 分区
上述 systemd unit 文件在 /boot 不在根文件系统上时会失败,因为 systemd 可能会在运行 kexec-load unit 文件之前卸载 /boot。另一种方法是加载一个“钩子” unit 文件,该文件在启动时什么也不做,并在终止时调用 kexec。通过使此 unit 文件与 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 冲突。
故障排除
系统在 “kexec_core: Starting new kernel” 后挂起或重启
General troubleshooting#Boot problems 中的故障排除信息可能有助于诊断问题。
在某些情况下,系统挂起可能是与 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 映像和内核的名称。
在 kexec 命令中添加 acpi_rsdp 内核参数已在 [1] 中提出,并且在某些情况下可以解决此问题,而无需通过 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