linux kdump
Kdump是获取崩溃的Linux内核转储的一种方法,但是要找到说明其用法和内部结构的文档可能会很困难。 在本文中,我将研究kdump使用的基础知识,并研究kdump / kexec内核实现的内部。
Kexec是Linux内核到内核的引导加载程序,可帮助从第一个内核的上下文引导第二个内核。 Kexec关闭第一个内核,绕过BIOS或固件阶段,然后跳转到第二个内核。 因此,在没有BIOS阶段的情况下,重新引导变得更快。
Kdump可以与kexec应用程序一起使用-例如,当第一个内核崩溃时引导第二个内核时,第二个内核用于复制第一个内核的内存转储,可以使用gdb和崩溃等工具分析该转储。确定恐慌的原因。 (在本文中,我将使用术语“ 第一内核 ”表示当前正在运行的内核,使用“ 第二内核”表示使用kexec运行的内核,并在当前内核出现紧急情况时捕获用于内核运行的内核。)
kexec机制在内核以及用户空间中具有组件。 内核为kexec重启功能提供了很少的系统调用。 一个名为kexec-tools的用户空间工具使用这些调用,并提供一个可执行文件来加载和引导第二个内核。 有时,发行版还会在kexec-tools之上添加包装器,这有助于捕获并保存各种转储目标配置的转储。 在本文中,我将使用名称distro-kexec-tools来避免上游kexec-tools和特定于发行版的kexec-tools代码之间的混淆。 我的示例将使用Fedora Linux发行版。
Fedora kexec工具
dnf install kexec- tools在Fedora机器上安装fedora-kexec-tools。 可以在安装fedora-kexec-tools之后执行systemctl start kdump来启动kdump服务。 启动此服务时,它将创建一个根文件系统(initramfs),该文件系统包含用于装载目标以保存vmcore的资源,以及一个将vmcore复制/转储到目标的命令。 然后,此服务将内核和initramfs加载到崩溃内核区域内的适当位置,以便可以在内核崩溃时执行它们。
Fedora包装器提供了两个用户配置文件:
- /etc/kdump.conf指定配置参数,其修改要求重新构造initramfs。 例如,如果将转储目标从本地磁盘更改为安装NFS的磁盘,则捕获内核将需要加载与NFS相关的内核模块。
- / etc / sysconfig / kdump指定不需要修改initramfs的配置参数。 例如,如果只需要修改传递给捕获内核的命令行参数,则无需重建initramfs。
如果在启动kdump服务后内核出现紧急情况,则将执行捕获内核,该内核将进一步从initramfs执行vmcore保存过程,然后重新启动到稳定的内核。
kexec工具
kexec-tools源代码的编译提供了一个称为kexec的可执行文件。 相同的可执行文件可用于加载和执行第二个内核,或加载捕获内核,可在内核出现紧急情况时执行。
加载第二个内核的典型命令:
# kexec -l kernel.img --initrd=initramfs-image.img –reuse-cmdline
--reuse-command line表示使用与第一个内核相同的命令行。 使用--initrd传递initramfs。 -l表示您正在加载第二个内核,该内核可以由kexec应用程序本身( kexec -e )执行。 使用-l加载的内核无法在内核崩溃时执行。 您必须传递-p而不是-l来加载可在内核崩溃时执行的捕获内核。
加载捕获内核的典型命令:
# kexec -p kernel.img --initrd=initramfs-image.img –reuse-cmdline
echo c> / pros / sysrq-trigger可用于崩溃内核以进行测试。 有关kexec-tools提供的选项的详细信息,请参见man kexec 。 在转到关注内部实现的下一部分之前,请观看以下kexec_dump演示:
Kdump:端到端流程
图1显示了流程图。 在第一个内核启动期间,必须为捕获内核保留Crashkernel内存。 您可以在内核命令行中传递crashkernel = Y @ X ,其中@X是可选的。 crashkernel = 256M可用于大多数x86_64系统; 但是,为崩溃内核选择合适的内存量取决于许多因素,例如内核大小和initramfs,以及initramfs中包含的模块和应用程序的运行时内存要求。 有关传递崩溃内核参数的更多方法,请参见内核参数文档 。
您可以将内核和initramfs映像传递到kexec可执行文件,如本节的典型命令( kexec-tools )所示。 捕获内核可以与第一个内核相同或不同。 通常,保持不变。 Initramfs是可选的; 例如,当内核使用CONFIG_INITRAMFS_SOURCE编译时,则不需要它。 通常,您保留与第一个initramfs不同的捕获initramfs,因为自动执行捕获initramfs中的vmcore副本会更好。 当执行kexec时 ,它还会加载elfcorehdr数据和炼狱可执行文件。 elfcorehdr具有有关系统RAM存储器组织的信息,炼狱是在捕获内核执行并验证第二阶段二进制文件/数据具有正确的SHA之前执行的二进制文件。 炼狱也可以设为可选。
当第一个内核崩溃时,它会执行最少的必要退出过程,如果存在则切换到炼狱。 炼狱验证加载的二进制文件的SHA256,如果正确,则将控制权传递给捕获内核。 捕获内核根据从elfcorehdr接收到的系统RAM信息创建vmcore。 因此,捕获内核启动后,您将在/ proc / vmcore中看到第一个内核的转储。 现在,根据您使用的initramfs,您可以分析转储,将其复制到任何磁盘,或者可以有自动复制,然后重新启动到稳定的内核。
内核系统调用
内核提供了两个系统调用,即kexec_load()和kexec_file_load() ,当执行kexec -l时可用于加载第二个内核。 它还为reboot()系统调用提供了一个额外的标志,可以使用kexec -e引导它进入第二个内核。
kexec_load() : kexec_load()系统调用会加载一个新内核,稍后可通过reboot()执行该内核。 其原型定义如下:
long kexec_load(unsigned long entry, unsigned long nr_segments,
struct kexec_segment *segments, unsigned long flags);
用户空间需要传递用于不同组件(例如内核,initramfs等)的段。因此, kexec可执行文件有助于准备这些段。 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 */
};
当使用LINUX_REBOOT_CMD_KEXEC调用reboot()时 ,它将引导到kexec_load()加载的内核中。 如果将标志KEXEC_ON_CRASH传递给kexec_load() ,则将不会通过reboot(LINUX_REBOOT_CMD_KEXEC)执行加载的内核。 而是在内核崩溃时执行。 必须将CONFIG_KEXEC定义为使用kexec,并且应为kdump定义CONFIG_CRASH_DUMP 。
kexec_file_load() :作为用户,您仅将两个参数(即内核和initramfs)传递给kexec可执行文件。 然后, kexec从sysfs或其他内核信息源读取数据并创建所有段。 因此, kexec_file_load()简化了用户空间,您仅在其中传递内核和initramfs的文件描述符。 所有段准备工作的其余部分都由内核本身完成。 应该启用CONFIG_KEXEC_FILE以使用此系统调用。 其原型如下:
long kexec_file_load(int kernel_fd, int initrd_fd, unsigned long
cmdline_len, const char __user * cmdline_ptr, unsigned long
flags);
请注意, kexec_file_load()也接受命令行,而kexec_load()不接受。 内核具有不同的体系结构特定的方式来接受命令行。 因此,在使用kexec_load()的情况下, kexec-tools会将命令行通过段之一(如dtb或ELF引导说明等) 传递 。
当前,仅x86和PowerPC支持kexec_file_load() 。
内核崩溃时会发生什么
当第一个内核崩溃时,在将控制权传递给炼狱或捕获内核之前,它会执行以下操作:
- 准备CPU寄存器(请参阅内核代码中的crash_setup_regs() );
- 更新vmcoreinfo注释(请参阅crash_save_vmcoreinfo() );
- 关闭非崩溃的CPU并保存准备好的寄存器(请参见machine_crash_shutdown()和crash_save_cpu() );
- 您可能还需要在此处禁用中断控制器;
- 最后,它执行kexec重启(请参阅machine_kexec() ),该重启将kexec段加载/刷新到内存,并将控制权交给入口段的执行。 入口段可以是炼狱或下一个内核的起始地址。
ELF程序头
kdump中涉及的大多数转储核心都是ELF格式。 因此,了解ELF程序标头很重要,尤其是在您要查找vmcore准备问题时。 每个ELF文件都有一个程序头,该头是:
- 由系统加载程序读取,
- 描述了程序应如何加载到内存中,
- 可以使用Objdump -p elf_file查找程序头。
vmcore的ELF程序标头示例:
# objdump -p vmcore
vmcore:
file format elf64-littleaarch64
Program Header:
NOTE off 0x0000000000010000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**0 filesz
0x00000000000013e8 memsz 0x00000000000013e8 flags ---
LOAD off 0x0000000000020000 vaddr 0xffff000008080000 paddr 0x0000004000280000 align 2**0 filesz
0x0000000001460000 memsz 0x0000000001460000 flags rwx
LOAD off 0x0000000001480000 vaddr 0xffff800000200000 paddr 0x0000004000200000 align 2**0 filesz
0x000000007fc00000 memsz 0x000000007fc00000 flags rwx
LOAD off 0x0000000081080000 vaddr 0xffff8000ffe00000 paddr 0x00000040ffe00000 align 2**0 filesz
0x00000002fa7a0000 memsz 0x00000002fa7a0000 flags rwx
LOAD off 0x000000037b820000 vaddr 0xffff8003fa9e0000 paddr 0x00000043fa9e0000 align 2**0 filesz
0x0000000004fc0000 memsz 0x0000000004fc0000 flags rwx
LOAD off 0x00000003807e0000 vaddr 0xffff8003ff9b0000 paddr 0x00000043ff9b0000 align 2**0 filesz
0x0000000000010000 memsz 0x0000000000010000 flags rwx
LOAD off 0x00000003807f0000 vaddr 0xffff8003ff9f0000 paddr 0x00000043ff9f0000 align 2**0 filesz
0x0000000000610000 memsz 0x0000000000610000 flags rwx
在此示例中,有一个注释部分,其余为加载部分。 注释部分包含有关CPU注释的信息,加载部分包含有关复制的系统RAM组件的信息。
Vmcore以elfcorehdr开头 ,其结构与ELF程序标头相同。 请参见图2中的elfcorehdr表示:
kexec-tools读取/ sys / devices / system / cpu / cpu%d / crash_notes并为CPU PT_NOTE准备标头。 同样,它读取/ sys / kernel / vmcoreinfo并为vmcoreinfo PT_NOTE准备头,并从/ proc / iomem读取系统RAM值并准备内存PT_LOAD头。 当捕获内核接收到elfcorehdr时 ,它将从标头中提到的地址追加数据并准备vmcore。
崩溃提示
崩溃注解是每个CPU的区域,用于在系统崩溃时存储CPU状态。 它具有有关当前PID和CPU寄存器的信息。
vmcoreinfo
此注释部分包含各种内核调试信息,例如结构大小,符号值,页面大小等。这些值由捕获内核解析并嵌入到/ proc / vmcore中 。 vmcoreinfo主要由makedumpfile应用程序使用。 Linux内核中的include / linux / kexec.h具有定义新vmcoreinfo的宏。 一些示例宏如下所示:
- VMCOREINFO_PAGESIZE()
- VMCOREINFO_SYMBOL()
- VMCOREINFO_SIZE()
- VMCOREINFO_STRUCT_SIZE()
makedumpfile
vmcore中的许多信息(例如空闲页面)都没有用。 Makedumpfile是一个排除不必要页面的应用程序,例如:
- 充满零的页面,
- 缓存没有私有标志的页面(非私有缓存);
- 缓存带有私有标志的页面(私有缓存);
- 用户过程数据页面;
- 免费页面。
另外,makedumpfile在复制时会压缩/ proc / vmcore数据。 它还可以从转储中删除敏感的符号信息; 但是,它首先需要内核的调试信息。 此调试信息来自VMLINUX或vmcoreinfo ,其输出可以是ELF格式或kdump压缩格式。
典型用法:
# makedumpfile -l --message-level 1 -d 31 /proc/vmcore makedumpfilecore
有关详细信息,请参见man makedumpfile 。
调试kdump问题
新的kdump用户可能遇到的问题:
Kexec -p kernel_image没有成功
- 检查是否分配了崩溃内存。
- cat / sys / kernel / kexec_crash_size不应为零。
- 猫/ proc / iomem | grep“崩溃内核”应具有分配的范围。
- 如果未分配,则在命令行中传递适当的crashkernel =参数。
- 如果未显示任何内容,则在kexec命令中传递-d并与kexec-tools邮件列表共享调试输出。
从第一个内核发出最后一条消息后,控制台上看不到任何内容(例如“再见”)
- 检查kexec -l kernel_image后跟kexec -e是否有效。
- 可能缺少特定于体系结构或计算机的选项。
- 炼狱SHA验证可能失败。 如果您的体系结构不支持炼狱中的控制台,则很难调试。
- 可能第二个内核提前崩溃了。
- 将您的系统的Earlycon / earlyprintk选项传递到第二个内核命令行。
- 与kexec-tools邮件列表共享第一个和捕获内核的dmesg日志。
资源资源
fedora-kexec工具
- 仓库: git://pkgs.fedoraproject.org/kexec-tools
- 邮件列表: kexec@lists.fedoraproject.org
- 描述:Specs文件和脚本提供了用户友好的命令/服务,以便可以在不同的用户场景中自动执行kexec-tools 。
kexec工具
- 仓库:git://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git
- 邮件列表: kexec@lists.infradead.org
- 说明:使用内核系统调用并提供用户命令kexec 。
Linux内核
- 仓库: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
- 邮件列表: kexec@lists.infradead.org
- 说明:实现kexec_load() , kexec_file_load()和reboot()系统调用以及特定于体系结构的代码,例如machine_kexec()和machine_crash_shutdown()等。
Makedump文件
- 仓库: git://git.code.sf.net/p/makedumpfile/code
- 邮件列表: kexec@lists.infradead.org
- 说明:从转储文件中压缩和过滤不必要的组件。
2017年6月20日在LinuxCon ContainerCon CloudOpen中国上的Pratyush Anand的KDUMP:使用和内部知识讲座中了解更多信息。
翻译自: https://opensource.com/article/17/6/kdump-usage-and-internals
linux kdump