Linux Core Dump

發表於2016-11-15

當程式執行的過程中異常終止或崩潰,作業系統會將程式當時的記憶體狀態記錄下來,儲存在一個檔案中,這種行為就叫做Core Dump(中文有的翻譯成“核心轉儲”)。我們可以認為 core dump 是“記憶體快照”,但實際上,除了記憶體資訊之外,還有些關鍵的程式執行狀態也會同時 dump 下來,例如暫存器資訊(包括程式指標、棧指標等)、記憶體管理資訊、其他處理器和作業系統狀態和資訊。core dump 對於程式設計人員診斷和除錯程式是非常有幫助的,因為對於有些程式錯誤是很難重現的,例如指標異常,而 core dump 檔案可以再現程式出錯時的情景。

Core Dump 名詞解釋

在半導體作為電腦記憶體材料之前,電腦記憶體使用的是 磁芯記憶體(Magnetic Core Memory),Core Dump 中的 Core 沿用了磁芯記憶體的 Core 表達。圖為磁芯記憶體的一個單元,來自 Wikipedia.

在 APUE 一書中作者有句話這樣寫的:

Because the file is named core, it shows how long this feature has been part of the Unix System.

這裡的 core 就是沿用的是早期電腦磁芯記憶體中的表達,也能看出 Unix 系統 Core Dump 機制的悠久歷史。

Dump 指的是拷貝一種儲存介質中的部分內容到另一個儲存介質,或者將內容列印、顯示或者其它輸出裝置。dump 出來的內容是格式化的,可以使用一些工具來解析它。

現代作業系統中,用 Core Dump 表示當程式異常終止或崩潰時,將程式此時的記憶體中的內容拷貝到磁碟檔案中儲存,以方便程式設計人員除錯。

Core Dump 如何產生

上面說當程式執行過程中異常終止崩潰時會發生 core dump,但還沒說到什麼具體的情景程式會發生異常終止或崩潰,例如我們使用 kill -9 命令殺死一個程式會發生 core dump 嗎?實驗證明是不能的,那麼什麼情況會產生呢?

Linux 中訊號是一種非同步事件處理的機制,每種訊號對應有其預設的操作,你可以在 這裡 檢視 Linux 系統提供的訊號以及預設處理。預設操作主要包括忽略該訊號(Ingore)、暫停程式(Stop)、終止程式(Terminate)、終止併發生core dump(core)等。如果我們訊號均是採用預設操作,那麼,以下列出幾種訊號,它們在發生時會產生 core dump:

Signal Action Comment
SIGQUIT Core Quit from keyboard
SIGILL Core Illegal Instruction
SIGABRT Core Abort signal from abort
SIGSEGV Core Invalid memory reference
SIGTRAP Core Trace/breakpoint trap

當然不僅限於上面的幾種訊號。這就是為什麼我們使用 Ctrl+z 來掛起一個程式或者 Ctrl+C 結束一個程式均不會產生 core dump,因為前者會向程式發出 SIGTSTP 訊號,該訊號的預設操作為暫停程式(Stop Process);後者會向程式發出SIGINT 訊號,該訊號預設操作為終止程式(Terminate Process)。

同樣上面提到的 kill -9 命令會發出 SIGKILL 命令,該命令預設為終止程式。而如果我們使用 Ctrl+ 來終止一個程式,會向程式發出 SIGQUIT 訊號,預設是會產生 core dump 的。還有其它情景會產生 core dump, 如:程式呼叫 abort() 函式、訪存錯誤、非法指令等等。

下面舉兩個例子來說明:

  • 終端下比較 Ctrl+C 和 Ctrl+
  • 小程式產生 core dump

Linux 下開啟 Core Dump

我使用的 Linux 發行版是 Ubuntu 13.04,設定生成 core dump 檔案的方法如下:

  • 開啟 core dump 功能
    • 在終端中輸入命令 ulimit -c ,輸出的結果為 0,說明預設是關閉 core dump 的,即當程式異常終止時,也不會生成 core dump 檔案。
    • 我們可以使用命令 ulimit -c unlimited 來開啟 core dump 功能,並且不限制 core dump 檔案的大小; 如果需要限制檔案的大小,將 unlimited 改成你想生成 core 檔案最大的大小,注意單位為 blocks(KB)。
    • 用上面命令只會對當前的終端環境有效,如果想需要永久生效,可以修改檔案 /etc/security/limits.conf檔案,關於此檔案的設定參看 這裡 。增加一行:
  • 修改 core 檔案儲存的路徑
    • 預設生成的 core 檔案儲存在可執行檔案所在的目錄下,檔名就為 core
    • 通過修改 /proc/sys/kernel/core_uses_pid 檔案可以讓生成 core 檔名是否自動加上 pid 號。
      例如 echo 1 > /proc/sys/kernel/core_uses_pid ,生成的 core 檔名將會變成 core.pid,其中 pid 表示該程式的 PID。
    • 還可以通過修改 /proc/sys/kernel/core_pattern 來控制生成 core 檔案儲存的位置以及檔名格式。
      例如可以用 echo "/tmp/corefile-%e-%p-%t" > /proc/sys/kernel/core_pattern 設定生成的 core 檔案儲存在 “/tmp/corefile” 目錄下,檔名格式為 “core-命令名-pid-時間戳”。這裡 有更多詳細的說明!

使用 gdb 除錯 Core 檔案

產生了 core 檔案,我們該如何使用該 Core 檔案進行除錯呢?Linux 中可以使用 GDB 來除錯 core 檔案,步驟如下:

  • 首先,使用 gcc 編譯原始檔,加上 -g 以增加除錯資訊;
  • 按照上面開啟 core dump 以使程式異常終止時能生成 core 檔案;
  • 執行程式,當core dump 之後,使用命令 gdb program core 來檢視 core 檔案,其中 program 為可執行程式名,core 為生成的 core 檔名。

下面用一個簡單的例子來說明:

編譯加上除錯資訊, 執行之後core dump, 使用 gdb 檢視 core 檔案.

從上面可以看出,我們可以還原 core_demo 執行時的場景,並使用 where 可以檢視當前程式呼叫函式棧幀, 還可以使用 gdb 中的命令檢視暫存器,變數等資訊.

參考資料

相關文章