Kdump 实现的基本原理Kdump 的实现可以分为两个部分:内核和用户工具。内核提供机制,用户工具在这些机制上实现各种转储策略。内核机制对用户工具的接口是一个系统调用:kexec_load(),它被用于加载捕获内核和传递一些相关信息。捕获内核启动后,会像一般内核一样,去运行为它创建的 ramdisk 上的 init 程序。而各种转储机制都可以事先在 init 中实现。为了在生产内核崩溃时能顺利启动捕获内核,捕获内核(以及它的 ramdisk)是事先放到生产内核的内存中的。而捕获内核启动后需要使用的一小部分内存是通过 crashkernel=Y@X 这一内核参数在生产内核中保存的。为了生产内核的内存不被捕获内核启动时破坏,同时省去额外编译一个内核用作捕获内核的麻烦,kerenl 又实现了可重定位内核(relocatable kernel)技术。
生产内核的内存是通过 /proc/vmcore 这个文件交给捕获内核的。为了生成它,用户工具先在生产内核中分析出内存的使用和分布等情况,然后把这些信息综合起来 生成一个 ELF 文件头保存起来。捕获内核被引导时会被同时传递这个 ELF 文件头的地址,通过分析它,捕获内核就可以生成出 /proc/vmcore。有了 /proc/vmcore 这个文件,捕获内核的 ramdisk 中的脚本就可以通过通常的文件读写和网络来实现 各种策略了。同时 kdump 的用户工具还提供了缩减内存镜像尺寸的工具。这就是 Kdump 的基本设计。
Kexec 详解用户空间工具kdump 的很大一部分工作都是在用户空间内完成的。与 kexec 相关的集中在一个叫“kexec-tools”的工具中的“kexec”程序中。该程序主要是为调用 kexec_load() 收集各种信息,然后调用之。这些信息主要包括 purgatory 的入口地址,还有一组由 struct kexec_segment 描述的信息,该结构体定义为 :
1
2
3
4
5
6
| struct kexec_segment {
const void *buf;
size_t bufsz;
const void *mem;
size_t memsz;
};
|
kernel 系统调用kexec 在 kernel 里以一个系统调用 kexec_load() 的形式提供给用户。这个系统调用主要用来把另一个内核和其 ramdisk 加载到当前内核中。在 kdump 中,捕获内核只能使用事先预留的一小段内存。生产内核的内存镜像会被以 /proc/vmcore 的形式提供给用户。这是一个 ELF 格式的方件,它的头是由用户空间工具 kexec 生成并传递来的。在系统崩溃时,系统最后会调用 machine_kexec()。这通常是一个硬件相关的函数。它会引导捕获内核,从而完成 kdump 的过程。
kdump 内存处理用于捕获内核的内存生产内核分析 cmdline 中的 crashkernel 参数后,调用 reserve_crashkernel() 来为捕获内核保存一段内存。这是一个 arch-dependent 的 function。保存之后,在 powerpc 上可以从 /proc/device-trees/chosen/linux,crashkernel-size 和 /proc/device-trees/chose/linux,crashkernel-base 中得到大小及位置信息。
捕获内核如何为 dump 工具提供生产内核的内存镜像/proc/vmcore ELF 格式
kexec 在加载捕获内核时,会计算并生成一个 ELF 文件头。这个 ELF 头含有生产内核的内存位置等等一系列信息。这个 ELF 头连同其他信息一起保存在由 reserve_crashkernel() 保留的那段内存中。当崩溃发生时,此 ELF 头的位置会被传给捕获内核,由它生成 /proc/vmcore 以供 保存。
/dev/oldmem raw 格式
captured kernel 启动后,还会用 raw 格式通过 /dev/oldmem 来提供生产内核的内存。用户态的工具可能要自己提取其中的 ELF header 以便得到 vmcore。但它的好处是可以只提取 vmcore 的一部分而不用 dump 出全部 vmcore。
可重定位内核(relocatable kernel)可重定位内核的意义在 kdump 出现之前,内核只能从一个固定的物理地址上启动。这对 kdump 来说是一种限制。因为为了收集生产内核的内存镜像,捕获内核不能从生产内核使用的启动地址上启动。因此就需要另编译一个从一个不同的地址启动的内核来作捕获内核。这就是为什么 RHEL5 中有一个包叫 kernel-kdump 的原因。技术的创新往往来自对方便的追求。开发人员为了不用费心多编译一个内核,为 kernel 实现了“可重定向”这个特性。
实现原理x86_64: 运行时修改 text 段及 data 段的眏射
kernel 在启动以后,会检测自己被加载到了什么位置。然后根据这个来更新自己的内存页表以反映 kernel 的 text 段和 data 段中虚拟地址与物理地址之间正确的映射关系。
i386: 使用预先生成的重定位信息
i386 中的 text 和 data 段是已经写死的线性映射区的一部分,要想使用修改页表的办法支持重定向是比较困难的。于是在编译内核时,另生成一份所有需要重定位的 symbol 的位置信息,放进 bzimage 格式的内核中。内核启动解压缩后,根据加载的地址和这份表来时行重定位。
powerpc: 将 vmlinuz 链接为“position-independent executable”形式
与 x86 体系不同,在 powerpc 体系中,/boot/vmlinuz 并不是一个 bzimage 格式的文件,它就是一个 ELF 格式的文件,而且启动机理也不尽相同。因此,在 powerpc 上主要是利用了“位置无关可执行”格式这一成熟技术来实现可重定位。
makedumpfile 简介有些服务器有着超大的内存,可能比它的硬盘的容量还大。为了转储这样的内存镜像,就有了 makedumpfile,它的最主要的用途就是减少转储的内存镜像的体积。它有两个手段达到这个目的:页面过滤和页面压缩。
页面过滤makedumpfile 在处理 /proc/vmcore 时,能够过滤掉这样一些内存页:
这些类型的页往往是无关紧要的。通过“-d”这个选项指定一个过滤等级来去掉相应的页,如果要去掉所有这些类型,需要指定“31”。
页面压缩makedumpfile 可以逐页地压缩 vmcore 中的内存页,在事后的分析中,crash 可以在分析到某页时才将其解压缩。 |