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--