跳转至内容

Kdump

来自 ArchWiki

Kdump 是一种标准的 Linux 机制,用于在内核崩溃时转储机器内存内容。Kdump 基于 Kexec。Kdump 利用两个内核:常规系统内核和 kdump 捕获内核(以下简称 kdump 内核)。系统内核是正常引导的内核,带有 crashkernel 参数 —— 我们需要告知系统内核预留一定量的物理内存,用于加载/执行 kdump 内核。然后必须提前加载 kdump 内核,因为当系统内核崩溃时,由于内核已损坏,没有可靠的方法从磁盘读取数据。

一旦发生内核崩溃,系统内核的崩溃处理程序将使用 Kexec 机制在预留内存中启动 kdump 内核。系统内核的内存在这种 kexec 引导中得以保留,并且在崩溃时刻对 kdump 内核可见。一旦 kdump 内核启动,用户可以通过收集 /proc/vmcore 文件来访问崩溃系统内核的内存。此类崩溃转储可以保存到磁盘,或通过网络复制到其他机器以进行进一步的死后分析 (post-mortem investigation)。

在服务器生产环境中,系统内核和 kdump 内核可能不同 —— 系统内核需要大量功能,并编译了许多内核标志/驱动程序,而 kdump 内核的目标是极简且占用尽可能少的内存(例如,如果仅将崩溃转储存储在磁盘上,则可以编译为不支持网络)。但对于桌面端以及一般的非特定设置,系统内核和 kdump 内核使用同一个内核。这意味着我们将加载两次相同的内核代码 —— 一次作为正常的系统内核,另一次加载到预留内存区域,但使用不同的内核参数。

设置 kdump 的替代方案

自动化方式:kdumpst

注意 kdumpst 隐式依赖于 GRUB,且不适用于其他 引导加载程序,详见 https://gitlab.freedesktop.org/gpiccoli/kdumpst/-/issues/21

kdumpstAUR 工具是一种加载 kdump 的自动化方式。它具有高度的可定制性 —— 默认情况下使用另一种日志收集方法(称为 pstore),但可以轻松设置为使用 kdump(只需在 /usr/share/kdumpst.d/00-default 中设置 USE_PSTORE_RAM=0)。如果 pstore RAM 区域不可用,该工具也会回退到 kdump。

安装 kdumpst 后,可以检查 journal 日志,出现以下消息意味着 kdump 已加载:kdumpst: panic kexec loaded successfully。如果发生内核崩溃,kdump 将被收集,并在随后的启动中显示操作成功的信息:kdumpst: logs saved in "/var/crash/kdumpst/logs"。在该文件夹中,用户将找到一个轻量级的 zip 压缩包,其中包含 dmesg 和一些额外数据。vmcore 本身保存在 /var/crash/kdumpst/crash。如有疑问/问题,可以使用 OFTC 的 #kdump IRC 频道,或在 kdumpst 仓库中提交 issue。

自动化方式:simple-kdump

simple-kdumpAUR 工具提供了一种简单且易于配置的方式来设置和收集 kdump。与 kdumpstAUR 不同,它不依赖于引导加载程序,只有一个目标:将 vmcore 文件保存到 /var/crash/

它基本上涵盖了后文提到的所有手动设置,但使用了 systemd 进行了更好的组织,并且复用了 Arch Linux 内核(或用户选择的任何内核),因此非常灵活且简单。

安装 simple-kdumpAUR 后,使用任何启用了 CONFIG_PROC_VMCORE=y 的引导内核/initramfs 组合填写 /etc/conf.d/simple-kdump.conf。建议使用 Arch Linux 的 linux 或 linux-lts 内核,因为它们已经启用了所有需要的功能。

然后添加 crashkernel=[size] 内核参数 并重启。建议使用不小于 512M 的值。

最后启用并启动 simple-kdump-setup.service,然后参考 #通过触发内核崩溃测试 kdump 来验证 kdump 的行为。

kexec 内核应进入 Emergency Mode to collect vmcore(收集 vmcore 的紧急模式)目标,并出现提示要求登录紧急 shell。您可以忽略该登录,因为 vmcore 的收集将在后台进行并自动重启。

重启后,/var/crash/crashdump-* 目录下应该会出现一个新的崩溃转储文件。

注意 /etc/conf.d/simple-kdump.conf 中的默认 BOOT_OPTIONS 是占位符 (root=/dev/os/root)。必须将其替换为您正常引导时使用的相同 root=, rootflags=, rootfstype= 以及任何解密参数,否则 kexec 内核无法挂载根文件系统,导致 /var/crash/ 保持为空。请不要包含 crashkernel=quiet;服务会自动附加 nr_cpus=1 reset_devices systemd.unit=emergency-kdump.target。编辑后,运行 systemctl restart simple-kdump-setup.service 以重新加载 kexec 镜像。
注意 如果根文件系统在 dm-crypt/LUKS 上,kexec 环境将在写入 vmcore 之前在控制台上提示输入密码。这除了 crashkernel 自身需要的内存外,还需要为 kexec 内核的密钥槽 (keyslot) 额外提供 1G 内存。用于 kdump 的 initramfs 中也必须包含 encrypt 钩子(或 sd-encrypt)(对于已经从 LUKS 引导的安装,默认已包含)。为了实现完全无人值守的捕获,请将转储存储在独立的、未加密的分区上并相应调整 simple-kdump-collect.service,或者通过嵌入在 initramfs 中的密钥文件临时解锁根设备(参见 Dm-crypt/设备加密#密钥文件)。

手动步骤

如果您倾向于手动操作,以下指南将提供帮助。

编译内核

系统内核和 kdump 内核都需要一些默认可能未设置的配置标志。请参阅 内核编译 文章以获取更多关于在 Arch 中编译自定义内核的信息。这里我们将重点介绍 Kdump 的特定配置。当前的 Arch 默认内核构建已设置这些标志。您可以通过查看 /proc/config.gz 来验证正在运行的内核是否设置了这些标志。

请注意,默认的 linuxlinux-lts 内核都启用了所需选项。但遗憾的是,默认内核剥离了调试信息,因此仍需要重新编译内核以保留所有调试信息,以便正确分析 vmcore。

要创建内核,您需要编辑内核 .config 文件并启用以下配置选项

.config
CONFIG_DEBUG_INFO=y
CONFIG_CRASH_DUMP=y
CONFIG_PROC_VMCORE=y
CONFIG_DEBUG_INFO=y
COFNIG_DEBUG_INFO_BTF=y

最后两个选项是为了提供额外的调试信息,以便 crashdrgn 等工具能够分析 vmcore。(或者是否有办法使用 debuginfod 下载内核调试信息?)

同时将软件包基础名称更改为 linux-kdump 之类的内容,以便将该内核与 Arch 默认内核区分开。编译内核包并安装。保存未压缩的系统内核二进制文件 ./src/linux-X.Y/vmlinux —— 它包含调试符号,您在以后分析崩溃时需要用到它。

关于构建 kdump 内核或配置 kdump 内核参数的详细信息,可参考内核 Kdump 文档。

复用现有内核和 initramfs

设置 kdump 最简单的方法是使用现有的内核和 initramfs。这里的示例将以 linux 内核为例,其 initramfs 生成在 /boot/initramfs-linux.img

其核心思想是将 kexec 环境的启动过程视为一个常规的 Arch Linux 启动序列。但通过额外的 systemd 选项稍微改变启动顺序(以跳过重新设置 kexec 环境,直接收集 vmcore 并重启)。

因此,与其它发行版不同,我们不需要生成特殊的 initramfs(而且由 mkinitcpio 生成的默认 initramfs 已经比竞争对手小得多)。

设置 kdump 内核

首先,您需要在系统内核中预留内存用于加载 kdump 内核。编辑您的引导加载程序配置并添加 crashkernel=[size] 内核参数

根据机器情况和 kdump 内核的构建方式,256M512M 通常就足够了 —— 在完成所有设置后尝试一下以检查是否成功。请注意,预留的内存对系统内核不可用。

重启进入系统内核。为了确保内核以正确的选项引导,请检查 /proc/cmdline/sys/kernel/kexec_crash_size 文件,确认内存确实已被预留(有时内存预留可能会失败,虽然罕见 —— 如果发生这种情况,请检查 dmesg 以获取更多信息)。

接下来,您需要告知 Kexec 您想要使用哪个 kdump 内核。指定您的内核、initramfs 文件、根设备以及其他必要参数:(此处我们使用默认的 linux 内核)

# kexec -p /boot/vmlinuz-linux --initrd=/boot/initramfs-linux.img] --append="root=[root-device] irqpoll nr_cpus=1 reset_devices"

它将 kdump 内核加载到预留区域。如果没有 -p 标志,kexec 会立即引导内核;但在有该标志的情况下,kdump 内核将被加载到预留内存中,其启动将推迟到发生崩溃为止。

注意 参数 nr_cpus=1 将 kdump 环境中的 CPU 限制为 1 个,这既能节省内存(CPU 结构会消耗内存!),也更安全,因为它限制了潜在并发问题的触发面。如果由于某种原因该选项失败,可以使用 maxcpus=1 代替。后者消耗的内存稍多,因为它初始化了其他 CPU 结构但禁用了除 CPU0 以外的所有 CPU,而 nr_cpus 则有效地丢弃了其他 CPU 结构。更多信息请参阅内核 CPU 热插拔 文档。

您可能不想手动运行 kexec,而是想设置一个在启动时运行 kexec 的 Systemd 服务

/etc/systemd/system/kdump.service
[Unit]
Description=Load the kdump kernel
After=local-fs.target

[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/usr/bin/kexec -p /boot/vmlinuz-linux --initrd=/boot/initramfs-linux.img --append="root=[root-device] irqpoll nr_cpus=1 reset_devices systemd.mask=kdump.service"
ExecStop=/usr/bin/kexec -p -u

[Install]
WantedBy=multi-user.target

然后 启用 kdump.service

注意,由于服务已启用,且我们的 kexec 环境启动方式与常规启动完全一致,它会尝试启动 kdump.service 但会因为内存不足而失败。因此,在 --append= 选项中指定了 systemd.mask=kdump.service 以避免启动 kdump 服务本身。

要检查崩溃内核是否已加载,请运行以下命令

$ cat /sys/kernel/kexec_crash_loaded

通过触发内核崩溃测试 kdump

如果您想测试崩溃,可以使用 sysrq

警告 内核崩溃可能会损坏磁盘上的数据,请自行承担风险!
# sync; echo 1 > /proc/sys/kernel/sysrq; echo c > /proc/sysrq-trigger

一旦发生崩溃,kexec 将加载您的 kdump 内核,其外观应与常规启动完全一致,但内存要小得多(预留的大小)且只有一个 CPU 核心。

保存崩溃内核的内存

引导进入 kdump 内核后,目的是保存 /proc/vmcore 中的相关内容以便以后分析。虽然它被呈现为一个文件(因此可以通过 cp /proc/vmcore /root/vmcore.crashdump 来复制),但这并不是推荐的方法。vmcore 是系统内存的完整副本,因此如果您的机器有 64G 内存,该文件将有 64G。它包含了所有加载的用户空间数据以及空闲内存。因此,保存它的最佳方法是使用 makedumpfile 工具。该程序能够移除空闲内存和无关的用户空间数据,并能压缩 vmcore!使用示例:

# makedumpfile -z -d 31 /proc/vmcore /root/vmcore.crashdump_compressed

您还可以使用此命令导出崩溃内核的 dmesg 日志

# makedumpfile --dump-dmesg /proc/vmcore /root/vmcore.dmesg

可以使用以下 systemd 服务自动保存崩溃转储并再次重启进入系统内核

/etc/systemd/system/kdump-save.service
[Unit]
Description=Save the kernel crash dump after a crash
After=multi-user.target

[Service]
Type=idle
ExecStart=/bin/sh -c 'mkdir -p /var/crash/ && /usr/bin/makedumpfile -z -d 31 /proc/vmcore "/var/crash/crashdump-$$(date +%%F-%%T)"'
ExecStopPost=/usr/bin/systemctl reboot
UMask=0077

这可以通过 kdump 内核命令行调用 —— 为此,我们需要如下编辑 kdump 加载服务

/etc/systemd/system/kdump.service
[Unit]
Description=Load the kdump kernel
After=local-fs.target

[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/usr/bin/kexec -p /boot/vmlinuz-linux --initrd=/boot/initramfs-linux.img --append="root=[root-device] irqpoll nr_cpus=1 reset_devices systemd.mask=kdump.service systemd.unit=kdump-save.service"
ExecStop=/usr/bin/kexec -p -u

[Install]
WantedBy=multi-user.target

使用 mkinitcpio 实现早期 kdump

您可能会遇到内核在 systemd 服务启动之前就崩溃的情况。在这种情况下,将 kexec 作为 mkinitcpio 钩子而非服务运行可能会有所帮助。

首先备份您的 initramfs。这将用于运行崩溃内核。

# cp /boot/initramfs-linux.img /boot/initramfs-linux-crash.img

接下来,创建 mkinitcpio 安装文件。这样我们可以在构建主 initramfs 的同时,为崩溃内核构建一个崩溃 initramfs 副本以及

/etc/initcpio/install/kdump
build() {
        add_binary kexec
        add_file /boot/initramfs-linux-crash.img /crash/initramfs.img
        add_file /boot/vmlinuz-linux /crash/vmlinuz
        add_runscript
}

help() {
        cat <<HELPEOF
Installs the crash kernel on boot
HELPEOF
}

接着,创建 mkinitcpio 钩子文件。这使 kexec 作为 earlyhook 运行,有望在内核任何部分崩溃之前执行。这里的一个重要笔记是,我们以紧急模式 (emergency mode) 运行内核,因为以救援 (rescue) 或正常模式运行内核可能会导致崩溃内核中再次发生相同的崩溃。

/etc/initcpio/hook/kdump
run_earlyhook() {
	msg 'Loading crash kernel..'
	if [ -e /crash/vmlinuz ]; then
		if [ -e /crash/initramfs.img ]; then
			kexec -p /crash/vmlinuz --initrd=/crash/initramfs.img --append="root=[root-device] irqpoll nr_cpus=1 reset_devices emergency"
		else
			msg 'No initramfs found'
		fi
	else
		msg 'No vmlinuz found'
	fi
}

现在使用新钩子运行 mkinitcpio

# mkinitcpio -A kdump

当崩溃发生时,您将被加载到紧急内核模式。输入密码后,您将进入终端。您首先需要做的是将根文件系统设为可写。

$ mount -o remount, rw /

现在您可以使用 makedumpfile 保存转储文件(参见 #保存崩溃内核的内存

分析内核核心转储 (core dump)

研究保存的内核核心转储(kernel core dump)的最佳方法是使用专门为此设计的工具。最常见的替代方案是基于 gdb 的 crash。按照以下方式运行 crash

$ crash vmlinux path/crash.dump

其中 vmlinux 应包含调试符号,以便从保存的崩溃转储中提取更多信息。

请参阅 man crash[1] 以获取更多关于调试实践的信息。

另一个近期的替代方案是 drgn,这是一个基于 Python 且完全可脚本化的工具,用于从 vmcore 中提取信息。

参见

© . This site is unofficial and not affiliated with Arch Linux.

Content is available under GNU Free Documentation License 1.3 or later unless otherwise noted.