使用 Kdump 檢查 Linux 內核崩潰
kdump 是獲取崩潰的 Linux 內核轉儲的一種方法,但是想找到解釋其使用和內部結構的文檔可能有點困難。在本文中,我將研究 kdump 的基本使用方法,和 kdump/kexec 在內核中是如何實現。
kexec 是一個 Linux 內核到內核的引導載入程序,可以幫助從第一個內核的上下文引導到第二個內核。kexec 會關閉第一個內核,繞過 BIOS 或固件階段,並跳轉到第二個內核。因此,在沒有 BIOS 階段的情況下,重新啟動變得更快。
kdump 可以與 kexec 應用程序一起使用 —— 例如,當第一個內核崩潰時第二個內核啟動,第二個內核用於複製第一個內核的內存轉儲,可以使用 gdb
和 crash
等工具分析崩潰的原因。(在本文中,我將使用術語「第一內核」作為當前運行的內核,「第二內核」 作為使用 kexec 運行的內核,「捕獲內核」 表示在當前內核崩潰時運行的內核。)
kexec 機制在內核以及用戶空間中都有組件。內核提供了幾個用於 kexec 重啟功能的系統調用。名為 kexec-tools 的用戶空間工具使用這些調用,並提供可執行文件來載入和引導「第二內核」。有的發行版還會在 kexec-tools 上添加封裝器,這有助於捕獲並保存各種轉儲目標配置的轉儲。在本文中,我將使用名為 distro-kexec-tools 的工具來避免上游 kexec 工具和特定於發行版的 kexec-tools 代碼之間的混淆。我的例子將使用 Fedora Linux 發行版。
Fedora kexec-tools 工具
使用 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-tools 工具
編譯 kexec-tools 的源代碼得到了一個名為 kexec
的可執行文件。這個同名的可執行文件可用於載入和執行「第二內核」,或載入「捕獲內核」,它可以在內核崩潰時執行。
載入「第二內核」的命令:
# kexec -l kernel.img --initrd=initramfs-image.img –reuse-cmdline
--reuse-command
參數表示使用與「第一內核」相同的命令行。使用 --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: 端到端流
下圖展示了流程圖。必須在引導「第一內核」期間為捕獲內核保留 crashkernel 的內存。您可以在內核命令行中傳遞 crashkernel=Y@X
,其中 @X
是可選的。crashkernel=256M
適用於大多數 x86_64 系統;然而,為崩潰內核選擇適當的內存取決於許多因素,如內核大小和 initramfs,以及 initramfs 中包含的模塊和應用程序運行時的內存需求。有關傳遞崩潰內核參數的更多方法,請參閱 kernel-parameters 文檔。
您可以將內核和 initramfs 鏡像傳遞給 kexec
可執行文件,如(kexec-tools
)部分的命令所示。「捕獲內核」可以與「第一內核」相同,也可以不同。通常,一樣即可。Initramfs 是可選的;例如,當內核使用 CONFIG_INITRAMFS_SOURCE
編譯時,您不需要它。通常,從第一個 initramfs 中保存一個不一樣的捕獲 initramfs,因為在捕獲 initramfs 中自動執行 vmcore 的副本能獲得更好的效果。當執行 kexec
時,它還載入了 elfcorehdr
數據和 purgatory 可執行文件(LCTT 譯註:purgatory 就是一個引導載入程序,是為 kdump 定作的。它被賦予了「煉獄」這樣一個古怪的名字應該只是一種調侃)。 elfcorehdr
具有關於系統內存組織的信息,而 purgatory 可以在「捕獲內核」執行之前執行並驗證第二階段的二進位或數據是否具有正確的 SHA。purgatory 也是可選的。
當「第一內核」崩潰時,它執行必要的退出過程並切換到 purgatory(如果存在)。purgatory 驗證載入二進位文件的 SHA256,如果是正確的,則將控制權傳遞給「捕獲內核」。「捕獲內核」根據從 elfcorehdr
接收到的系統內存信息創建 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;
/* 用戶空間緩衝區 */
size_t bufsz;
/* 用戶空間中的緩衝區長度 */
void *mem;
/* 內核的物理地址 */
size_t memsz;
/* 物理地址長度 */
};
當使用 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()
:作為用戶,你只需傳遞兩個參數(即 kernel
和 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 引導注釋等)中傳遞命令行。
目前,kexec_file_load()
僅支持 x86 和 PowerPC。
當內核崩潰時會發生什麼
當第一個內核崩潰時,在控制權傳遞給 purgatory 或「捕獲內核」之前,會執行以下操作:
- 準備 CPU 寄存器(參見內核代碼中的
crash_setup_regs()
); - 更新 vmcoreinfo 備註(請參閱
crash_save_vmcoreinfo()
); - 關閉非崩潰的 CPU 並保存準備好的寄存器(請參閱
machine_crash_shutdown()
和crash_save_cpu()
); - 您可能需要在此處禁用中斷控制器;
- 最後,它執行 kexec 重新啟動(請參閱
machine_kexec()
),它將載入或刷新 kexec 段到內存,並將控制權傳遞給進入段的執行文件。輸入段可以是下一個內核的 purgatory 或開始地址。
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
在這個例子中,有一個 note 段,其餘的是 load 段。note 段提供了有關 CPU 信息,load 段提供了關於複製的系統內存組件的信息。
vmcore 從 elfcorehdr
開始,它具有與 ELF 程序頭相同的結構。參見下圖中 elfcorehdr
的表示:
kexec-tools
讀取 /sys/devices/system/cpu/cpu%d/crash_notes
並準備 CPU PT_NOTE
的標頭。同樣,它讀取 /sys/kernel/vmcoreinfo
並準備 vmcoreinfo PT_NOTE
的標頭,從 /proc/iomem
讀取系統內存並準備存儲器 PT_LOAD
標頭。當「捕獲內核」接收到 elfcorehdr
時,它從標頭中提到的地址中讀取數據,並準備 vmcore。
Crash note
Crash notes 是每個 CPU 中用於在系統崩潰的情況下存儲 CPU 狀態的區域;它有關於當前 PID 和 CPU 寄存器的信息。
vmcoreinfo
該 note 段具有各種內核調試信息,如結構體大小、符號值、頁面大小等。這些值由捕獲內核解析並嵌入到 /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
不應該有零值。cat /proc/iomem | grep "Crash kernel"
應該有一個分配的範圍。- 如果未分配,則在命令行中傳遞正確的
crashkernel=
參數。 - 如果沒有顯示,則在
kexec
命令中傳遞參數-d
,並將輸出信息發送到 kexec-tools 郵件列表。
在「第一內核」的最後一個消息之後,在控制台上看不到任何東西(比如「bye」)
- 檢查
kexec -e
之後的kexec -l kernel_image
命令是否工作。 - 可能缺少支持的體系結構或特定機器的選項。
- 可能是 purgatory 的 SHA 驗證失敗。如果您的體系結構不支持 purgatory 中的控制台,則很難進行調試。
- 可能是「第二內核」早已崩潰。
- 將您的系統的
earlycon
或earlyprintk
選項傳遞給「第二內核」的命令行。 - 使用 kexec-tools 郵件列表共享第一個內核和捕獲內核的
dmesg
日誌。
資源
fedora-kexec-tools
- GitHub 倉庫:
git://pkgs.fedoraproject.org/kexec-tools
- 郵件列表:kexec@lists.fedoraproject.org
- 說明:Specs 文件和腳本提供了用戶友好的命令和服務,以便
kexec-tools
可以在不同的用戶場景下實現自動化。
kexec-tools
- GitHub 倉庫:git://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git
- 郵件列表:kexec@lists.infradead.org
- 說明:使用內核系統調用並提供用戶命令
kexec
。
Linux kernel
- GitHub 倉庫:
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()
。
Makedumpfile
- GitHub 倉庫:
git://git.code.sf.net/p/makedumpfile/code
- 郵件列表:kexec@lists.infradead.org
- 說明:從轉儲文件中壓縮和過濾不必要的組件。
(題圖:Penguin、 Boot,修改:Opensource.com. CC BY-SA 4.0)
作者簡介:
Pratyush Anand - Pratyush 正在以以為 Linux 內核專家的身份與 Red Hat 合作。他主要負責 Red Hat 產品和上游所面臨的幾個 kexec/kdump 問題。他還處理 Red Hat 支持的 ARM64 平台周圍的其他內核調試、跟蹤和性能問題。除了 Linux 內核,他還在上游的 kexec-tools 和 makedumpfile 項目中做出了貢獻。他是一名開源愛好者,並在教育機構舉辦志願者講座,促進了 FOSS。
via: https://opensource.com/article/17/6/kdump-usage-and-internals
作者:Pratyush Anand 譯者:firmianay 校對:wxy
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive