linux kexec 介绍
kexec的功能是用一个运行的内核去运行一个新内核,就像运行一个应用程序一样。这种机制因为跳过了bootloader,可以实现系统的快速重启。另外kdump也是基于kexec实现(示意如下)。
kexec的实现有几点难点:
- 在当前内核的上下文中,如何用新内核去替换现有内核?
- 正常复位启动过程中,设备会被复位(或初始化)到已知状态。跳过了复位阶段,那如何在新内核kexec启动时,保证设备状态的可靠?
kexec的使用示例如下,其分为2部分:kexec内核加载和kexec内核执行。
kexec -l /bzImage --initrd=/initrd.img.gz --append="ro nosmap loglevel=4 console=ttyS0,9600n8 acpi_rsdp=0x7b7fe014" kexec -e
kexec 内核加载
- 将内核镜像文件、根文件系统、命令行参数等segment加载到用户态内存;
- 对segment sha256检查,确保内核数据没有被破坏;如果是kdump,备份相关数据到备份区(如i386,最开始的640K配置数据用于SMP内核启动,需要备份;或者powerpc,固定位置的异常向量等信息需要备份等)。这个环节又叫purgatory,其流程可以在用户态控制实现,也可以在内核态控制实现(如使能--kexec-file-syscall选项);
- 分配内核态页面,并将segment从用户态内存copy到内核态页面;如果使能--kexec-file-syscall选项,则跳过前面两步,直接将segment加载到内核页面,并进行purgatory。
- 分配页面并初始化image->control_code_page,对其建立页表,为下一步进行覆盖当前内核时代码寻址的页表映射。初始化LEVEL4/3/2/1 4层页表,并建立映射关系(如下图所示)。
kexec 内核运行
- 调用设备驱动shutdown接口关闭设备;
- 关闭中断,如IO-APIC, local irq, LAPIC;
- 关闭非0号CPU核;
- 清空TLB(此处以下代码为汇编实现,具体实现与arch相关,一般名叫relocate_new_kernel);
- 设置段寄存器、gdt、idt等;
- 建立一个新栈,并将新内核的入口地址压入栈中;
- 设置cr0寄存器:使能分页功能和页面保护功能;
- 设置cr4寄存器:使能扩展地址;
- 设置cr3寄存器,使cr3指向新的页表根目录;设置后,从此就与旧内核bye bye了;
- 将内核segment页面copy到指定位置,覆盖当前内核;
- 调用ret指令,从栈中弹出之前压栈的新内核bzImage入口地址,进入新内核引导;
kexec 调试注意实现
- kexec不会同步或卸载文件系统,此过程需要用户来保证;
- 从上面的流程中可以看出,kexec不会对cpu或设备进行复位,但系统重启过程中会调用reboot_notifier_list,所以register_reboot_notifier注册的接口不要有执行CPU复位等操作;
- 为了保证设备在kexec时处于稳定状态,kexec会调用设备驱动的shutdown接口来关闭,确保用户自己的设备驱动提供了正确的shutdown接口,或用户通过其他方式进行关闭;
--EOF--
时间: 2024-10-03 22:42:55