分析操作系统crash或hang的原因,需要用到kernel dump。Linux系统用来捕捉kernel dump的工具是kdump。
kdump的原理是启动一个特殊的dump-capture kernel把系统内存里的数据保存到文件里,为什么需要一个特殊的dump-capture kernel呢?因为原来的kernel已经出问题了,发生crash或hang了。
Dump-capture kernel 既可以是独立的,也可以与系统内核集成在一起–这需要硬件支持relocatable kernel才行。在X86_64系统上RHEL6/7和SLES11/12缺省都是与系统内核集成在一起的。
kdump工作的过程如下:
- 系统内核启动的时候,要给dump-capture kernel预留一块内存空间;
- 内核启动完成后,kdump service执行 kexec -p 命令把dump-capture kernel载入预留的内存里;
- 然后,如果系统发生crash,会自动reboot进入dump-capture kernel,dump-capture kernel只使用自己的预留内存,确保其余的内存数据不会被改动,它的任务是把系统内存里的数据写入到dump文件,比如/var/crash/vmcore,为了减小文件的大小,它会通过makedumpfile(8)命令对内存数据进行挑选和压缩;
- dump文件写完之后,dump-capture kernel自动reboot。
预留内存
配置kdump的一个关键环节是预留内存。预留的内存是有特殊要求的,它必须是连续的,在老的系统上比如SLES11还要求内存物理地址低于4GB(SLES12无此要求,因为新kernel允许使用高位内存)。可供预留的内存取决于硬件配置,BIOS/firmware占用的内存是不能预留的,EFI经常占用很多内存,I/O卡也要占用内存,在引导过程中都能看到:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
BIOS占用的: <6>[ 0.000000] BIOS-e820: 0000000000000000 - 0000000000092000 (usable) <6>[ 0.000000] BIOS-e820: 0000000000092000 - 0000000000094000 (reserved) <6>[ 0.000000] BIOS-e820: 0000000000094000 - 00000000000a0000 (usable) <6>[ 0.000000] BIOS-e820: 0000000000100000 - 000000006bbfd000 (usable) <6>[ 0.000000] BIOS-e820: 000000006bbfd000 - 000000006cbfd000 (reserved) <6>[ 0.000000] BIOS-e820: 000000006cbfd000 - 000000006cbfe000 (usable) <6>[ 0.000000] BIOS-e820: 000000006cbfe000 - 000000006cc7f000 (reserved) <6>[ 0.000000] BIOS-e820: 000000006cc7f000 - 00000000718ff000 (usable) <6>[ 0.000000] BIOS-e820: 00000000718ff000 - 00000000725ff000 (reserved) <6>[ 0.000000] BIOS-e820: 00000000725ff000 - 00000000735ff000 (ACPI NVS) <6>[ 0.000000] BIOS-e820: 00000000735ff000 - 00000000737ff000 (ACPI data) <6>[ 0.000000] BIOS-e820: 00000000737ff000 - 000000007b800000 (usable) <6>[ 0.000000] BIOS-e820: 0000000080000000 - 0000000090000000 (reserved) <6>[ 0.000000] BIOS-e820: 0000000100000000 - 0000018080000000 (usable) EFI占用的: <6>[ 0.000000] EFI: mem04: type=7, attr=0xf, range=[0x0000000000100000-0x0000000001000000) (15MB) <6>[ 0.000000] EFI: mem05: type=2, attr=0xf, range=[0x0000000001000000-0x0000000002364000) (19MB) <6>[ 0.000000] EFI: mem06: type=7, attr=0xf, range=[0x0000000002364000-0x0000000010000000) (220MB) <6>[ 0.000000] EFI: mem07: type=3, attr=0xf, range=[0x0000000010000000-0x000000001006b000) (0MB) <6>[ 0.000000] EFI: mem08: type=7, attr=0xf, range=[0x000000001006b000-0x0000000024f57000) (334MB) <6>[ 0.000000] EFI: mem09: type=2, attr=0xf, range=[0x0000000024f57000-0x0000000040100000) (433MB) <6>[ 0.000000] EFI: mem10: type=4, attr=0xf, range=[0x0000000040100000-0x0000000040180000) (0MB) <6>[ 0.000000] EFI: mem11: type=7, attr=0xf, range=[0x0000000040180000-0x0000000069352000) (657MB) <6>[ 0.000000] EFI: mem12: type=4, attr=0xf, range=[0x0000000069352000-0x00000000693d4000) (0MB) |
为dump-capture kernel预留内存的方法是在kernel command line中加入如下参数:
crashkernel=size[@offset]
需要预留多少内存呢?
RHEL从6.2开始可以使用”crashkernel=auto”,让内核自行计算,如果有问题才手工指定。
SLES12则可以用”kdumptool calibrate”命令计算推荐值,具体方法参见SUSE DOC: Calculating crashkernel Allocation Size 。
更老的系统上需要根据系统总内存手工计算预留内存的大小,参考值请自行搜索。
不幸的是,确实有些系统存在无法预留足够内存的情况,如果dump-capture kernel需要的内存比较多,而硬件配置又比较复杂导致可用的连续内存不足,就有可能发生。这在老内核上更常见,而新内核允许使用高位内存就好多了。
通常RHEL 6.X最大建议768MB,实际上代码中最大的限制是896MB,但实际经验中800M以上就会有各种问题。
1 2 3 4 5 6 7 8 9 |
reserve_crashkernel() ... 0585 if (crash_size >= KEXEC_RESERVE_UPPER_LIMIT) { 0586 pr_info("crashkernel reservation failed. " 0587 "specified size is too big.\n"); 0588 return; 0589 } #define KEXEC_RESERVE_UPPER_LIMIT (896 * 1024 * 1024) |
Redhat还提供了一个设置kdump的助手工具:
https://access.redhat.com/labs/kdumphelper/
kdump的性能
计算机的内存越来越大,kernel dump也越来越大,保存dump文件的时间也越来越长,为了提高速度,kdump的配置可以进行调整。
1,多CPU
缺省情况下dump-capture kernel只使用一个CPU,有时使用多个CPU会有帮助。这可以通过修改dump-capture kernel 的参数 nr_cpus=1 来实现,怎样修改dump-capture kernel的参数呢?不是在grub中,那里是普通kernel的参数,而是在 /etc/sysconfig/kdump 中,如下所示:
KDUMP_COMMANDLINE_APPEND=”irqpoll nr_cpus=4 …”
2,压缩算法
makedumpfile(8)默认的”-c”参数使用zlib,虽然压缩比很高但是速度很慢,通常低于30MB/s。可以选用速度更快的LZO,虽然压缩比稍微低一点但是速度可达800MB/s,把/etc/kdump.conf中的makedumpfile(8)的”-c”参数换成”-l”即可。
3,排除不需要的内存页
makedumpfile(8)的”-d”参数指定dump level,dump level是一个5-bit的编码,每个bit表示一种可以排除的内存页:
1 : Exclude the pages filled with zero.
2 : Exclude the non-private cache pages.
4 : Exclude all cache pages.
8 : Exclude the user process data pages.
16 : Exclude the free pages.
一般默认dump level是31,即排除以上所有类型的内存页。
参考资料:
http://lse.sourceforge.net/kdump/documentation/ols2oo5-kdump-paper.pdf
https://www.kernel.org/doc/Documentation/kdump/kdump.txt
http://people.redhat.com/nhorman/papers/ols-slides.pdf
http://events.linuxfoundation.org/sites/events/files/slides/slide_final_0.pdf