杂谈
百分之百肯定的是,如果你的内核崩溃了,没有发生重启,那就说明重启内核是有问题的,谁来控制重启内核呢,答案是 kexec 工具。
kexec 工具被执行起来后,如果指定的新内核文件没有问题,就会默默的将这个内核文件保存到内核空间里,类似于open,然后系统发生死机后,就可以转生。
user kexec
和内核交互的工具,本质上是一个系统调用。kexec 做了那些事情?
1 获取 reserved 内存块。读取/proc/iomem中的Crash kernel字段的信息。这步失败就嗝屁了。
2 把指定的内核吃到自己定义的变量里。名字为 struct kexec_info info,包含代码段和加载地址,还有其他信息比如设备树,启动参数cmdline。指定的加载地址区间来自reserved 内存块。
3 设置 vmcore的生成开关 —— elfcorehdr。对于arm64来讲,具体表现在设备树中添加节点 linux,elfcorehdr;其他架构的话是在启动参数中添加 elfcorehdr= 。这些都会保存在kexec_info中
上面是内核起死回生并且生成vmcore的必要条件。
这里代码撸的比较头疼,结构体信息太多,和workqueue一个鬼样。但是里面的一些机理类似 memblock 玩法,还有一个机理就是elf文件各个段的解析。
kernel kexec
内核层会完成kexec_info接收,设置新内核实体。
kexec会陷入系统调用函数
SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments,
struct kexec_segment __user *, segments, unsigned long, flags)
主函数为 do_kexec_load。下面进入代码欣赏环节。以kexec -p举例,设置-p等于设置KEXEC_ON_CRASH
do_kexec_load:
struct kimage **dest_image, *image;
if (flags & KEXEC_ON_CRASH) {
dest_image = &kexec_crash_image; //新内核 kexec_crash_image
}
if (flags & KEXEC_ON_CRASH) {
kimage_free(xchg(&kexec_crash_image, NULL)); //清除新内核内容
}
//将kexec_info读进来,存到image
ret = kimage_alloc_init(&image, entry, nr_segments, segments, flags);
//将各个段存到指定的加载地址。使用kmap。本质上就是从加载地址获取一个page,转为地址,然后存到该地址
//如果预留内存不足,返回-ENOMEM
for (i = 0; i < nr_segments; i++) {
ret = kimage_load_segment(image, &image->segment[i]);
}
image = xchg(dest_image, image); //到此,新内核 kexec_crash_image 加载完成。
各个段的存放地址是在reserved内存中
panic后会加载新内核
__crash_kexec:
if (kexec_crash_image) {
machine_kexec(kexec_crash_image);
}
读取新内核信息,然后热重启。重启与架构有关。 arm64会执行 cpu_soft_restart,然后调用到汇编。
vmcore
至于vmcore的生成是initcall函数自动完成的。
static int __init vmcore_init(void)
{
if (!(is_vmcore_usable()))
return rc;
rc = parse_crash_elf_headers();
if (rc) {
pr_warn("Kdump: vmcore not initialized\n");
return rc;
}
elfcorehdr_free(elfcorehdr_addr);
elfcorehdr_addr = ELFCORE_ADDR_ERR;
//创建/proc/vmcore
proc_vmcore = proc_create("vmcore", S_IRUSR, NULL, &proc_vmcore_operations);
if (proc_vmcore)
proc_vmcore->size = vmcore_size;
return 0;
}
fs_initcall(vmcore_init);
is_vmcore_usable 会检查 elfcorehdr, kexec执行的时候做了这件事。
结语
是不是和open差不多?是不是copy_from_user?就问像不像吧。
arm64 流程
启动A内核 ——> 执行 kexec 程序读取内核,并将各个段指定到reserved内存地址 ,打包成kexec_info——> 陷入系统调用,内核读取kexec_info,并保存B内核,并使能vmcore ——> 发生panic,内核启动B内核 ——> 生成vmcore
公众号:linux码头