什么是Kdump?
Kdump是一个基于kexec的内核崩溃转储机制,当系统崩溃时,kdump使用kexec启动到第二个内核。地热个内核叫做捕获内核或者又叫“2nd kernel”,它以很少的内存启动捕获内核,并捕获转储镜像。Kdump的概念是目前最可靠的内核转储技术,已被主要的linux厂商使用。(例如Red Hat系列)
什么是Kexec?
Kexec是一种能够根据已经运行内核的上下文快速启动新内核的一种机制,而不经过BIOS。BIOS的启动在一些大型机器或者有大量外设的机器上时特别耗时。这种机制能够节省需要在不同内核之间切换的开发人员的时间。
Kexec在内核空间和用户空间都有对应的组件,内核提供了几个kexec重启功能的系统调用。用户空间的软件包"kexec-tools"使用这些系统调用,并执行加载和引导第二个内核(捕获内核)。
Kexec由两部分组成,一是内核空间的系统调用kexec_load,负责在生产内核(或者叫第一个内核)启动时将捕获内核(或者叫第二个内核)加载到指定的位置。而是用户空间的kexec-tools,它将捕获内核(second kernel)的地址传递给生产内核(first kernel),让系统在崩溃的时候能够找到捕获内核(second kernel)的地址并运行。
Kdump怎么工作?
在当前系统发生崩溃时,新的捕获内核被加载,然后根据已设置的命令去将当前之前发生崩溃的系统的内存保存到一个特殊的文件(vmcore)中。
下面以Fedora26做为测试演示。(Fedora和CentOS系列的系统已经在kenrel中打开了CONFIG_KEXEC*选项。)
首先需要在系统启动时预留出给第二个内核运行的内存。内核参数"crashkernel=160M"会在系统启动时预留出160M内存的空间给捕获内核运行使用。"crashkerel=xM"还支持其他的参数,详细的可以参考内核参数文档 内核参数。
# dmesg | grep -i reserving
[ 0.000000] Reserving 160MB of memory at 656MB for crashkernel (System RAM: 2047MB)
系统启动后我们可以从上面的命令中看到已经预留出了160M的内存从内存的656M处。
安装用户态的包"kexec-tools",软件包中会提供kdump所需的服务和"kexec"快速内核启动命令,和压缩过滤内存的"makedumpfile"命令。
[root@localhost ~]# dnf install -y kexec-tools
配置,修改kdump相关的配置文件。
[root@localhost ~]# grep -v ^# /etc/kdump.conf
path /var/crash
core_collector makedumpfile -l --message-level 1 -d 31
[root@localhost ~]# grep -v ^# /etc/sysconfig/kdump
KDUMP_KERNELVER=""
KDUMP_COMMANDLINE=""
KDUMP_COMMANDLINE_REMOVE="hugepages hugepagesz slub_debug quiet"
KDUMP_COMMANDLINE_APPEND="irqpoll nr_cpus=1 reset_devices cgroup_disable=memory mce=off numa=off udev.children-max=2 panic=10 rootflags=nofail acpi_no_memhotplug transparent_hugepage=never nokaslr"
KEXEC_ARGS=""
KDUMP_IMG="vmlinuz"
KDUMP_IMG_EXT=""
配置文件/etc/kdump.conf设置了kdump发生时vmcore文件的存储方式,此文件修改后需要重启kdump的服务。
配置文件/etc/sysconfig/kdump,如果只是修改了COMMANDLINE相关的参数,则不需要去重新build生成新的initramfs文件。
启动kdump服务:
[root@localhost ~]# systemctl restart kdump
[root@localhost ~]# systemctl status kdump
● kdump.service - Crash recovery kernel arming
Loaded: loaded (/usr/lib/systemd/system/kdump.service; enabled; vendor preset: disabled)
Active: active (exited) since Sat 2017-07-15 10:46:22 UTC; 36s ago
Process: 2172 ExecStop=/usr/bin/kdumpctl stop (code=exited, status=0/SUCCESS)
Process: 2180 ExecStart=/usr/bin/kdumpctl start (code=exited, status=0/SUCCESS)
Main PID: 2180 (code=exited, status=0/SUCCESS)
Jul 15 10:46:21 localhost dracut[4264]: -rw-r--r-- 1 root root 127 Mar 28 02:15 usr/share/zoneinfo/Etc/UTC
Jul 15 10:46:21 localhost dracut[4264]: drwxr-xr-x 3 root root 0 Jun 22 13:38 var
Jul 15 10:46:21 localhost dracut[4264]: lrwxrwxrwx 1 root root 11 Jun 22 13:38 var/lock -> ../run/lock
Jul 15 10:46:21 localhost dracut[4264]: lrwxrwxrwx 1 root root 6 Jun 22 13:38 var/run -> ../run
Jul 15 10:46:21 localhost dracut[4264]: drwxr-xr-x 2 root root 0 Jun 22 13:38 var/tmp
Jul 15 10:46:21 localhost dracut[4264]: ========================================================================
Jul 15 10:46:21 localhost dracut[4264]: *** Creating initramfs image file '/boot/initramfs-4.11.9-300.fc26.x86_64kdump.img' done ***
Jul 15 10:46:22 localhost kdumpctl[2180]: kexec: loaded kdump kernel
Jul 15 10:46:22 localhost kdumpctl[2180]: Starting kdump: [OK]
Jul 15 10:46:22 localhost systemd[1]: Started Crash recovery kernel arming.
所有的服务都配置完成,如果此时系统发生了panic或者其他的一些导致系统崩溃的现象,这是kdump服务会将当时的内存镜像按照用户的配置保存起来。一个简单的方式是通过命令来触发:
[root@localhost ~]# echo c > /proc/sysrq-trigger
[some console log]
... ...
Starting Kdump Vmcore Save Service...
kdump: dump target is /dev/vda1
kdump: saving to /sysroot//var/crash/127.0.0.1-2017-07-16-04:21:36/
[ 2.718001] EXT4-fs (vda1): re-mounted. Opts: data=ordered
kdump: saving vmcore-dmesg.txt
kdump: saving vmcore-dmesg.txt complete
kdump: saving vmcore
Copying data : [100.0 %] -
kdump: saving vmcore complete
... ...
[/some console log]
当系统重启后就能在指定的目录下看到生成的vmcore文件。可以参考配置文件"/etc/kdump.conf"里的"path"字段。
[root@localhost ~]# ls -lt /var/crash/*/
total 33492
-rw-------. 1 root root 34253115 Jul 16 04:21 vmcore
-rw-r--r--. 1 root root 40360 Jul 16 04:21 vmcore-dmesg.txt
转储文件被保存后可以用"crash"这个软件包来分析这个"vmcore"文件。
开始提到了Kexec内核部分提供了一些系统调用,"kexec_load()" 和 "kexec_file_load()",其中一个用来加载捕获内核 - "kexec -l",另外一个来提供系统重启 - "kexec -e"。
系统调用"kexec_load()"可以加载一个新的内核并之后能够被"reboot()"调用。它是被这样定义的:
long kexec_load(unsigned long entry, unsigned long nr_segments,
struct kexec_segment *segments, unsigned long flags);
其中一个比较重要的是"kexec_segment"结构体:
struct kexec_segment {
void *buf; /* Buffer in user space */
size_t bufsz; /* Buffer length in user space */
void *mem; /* Physical address of kernel */
size_t memsz; /* Physical address length */
};
当reboot()的参数为"LINUX_REBOOT_CMD_KEXEC"并被调用时,则启动新的内核时就调用"kexec_load()"系统调用。另外“CONFIG_KEXEC”必须在编译kernel时被打开。
系统调用"kexec_load_file()"会设置2个参数"kernel"和"initramfs"给"kexec"命令。"kexec"会读取这些数据来创建对应的数据段。
long kexec_file_load(int kernel_fd, int initrd_fd,
unsigned long cmdline_len, const char *cmdline,
unsigned long flags);
同样的"CONFIG_KEXEC_FILE"参数也要在内核编译时被打开。
目前的大多数发行版都已经打开了"KEXEC"相关的配置参数。
以上就是一个kdump的简单流程。有关"kdump"能够捕获到的内核崩溃时间可以参考文档"/usr/share/doc/kexec-tools/kexec-kdump-howto.txt"。或者请参考我们写的测试用例 kdump-test。
参考资料:
kdump-paper
kdump-introduction
fedora-kexec-tools