天天看點

Linux Core Dump

當程式運作的過程中異常終止或崩潰,作業系統會将程式當時的記憶體狀态記錄下來,儲存在一個檔案中,這種行為就叫做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 Illegal Instruction
SIGABRT Abort signal from abort
SIGSEGV Invalid memory reference
SIGTRAP 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+​

    ​:

1

2

3

4

5

6

guohailin@guohailin:~$ sleep 10        #使用sleep指令休眠 10 s

^C                           #使用 Ctrl+C 終止該程式,不會産生 core dump

guohailin@guohailin:~$ sleep 10

^Quit (core dumped)                #使用 Ctrl+ 退出程式, 會産生 core dump(用kill -3 PID)

guohailin@guohailin:~$ ls         #多出下面一個 core 檔案

-rw-------  1 guohailin guohailin 335872 10月 22 11:31 sleep.core.21990

  • 小程式産生 core dump

7

8

#include

int main()

{

    int *null_ptr = NULL;

    *null_ptr = 10;            //對空指針指向的記憶體區域寫,會發生段錯誤

    return 0;

}

#編譯執行

guohailin@guohailin:~$ ./a.out

Segmentation fault (core dumped)

guohailin@guohailin:~$ ls      #多出下面一個 core 檔案

-rw-------  1 guohailin guohailin 200704 10月 22 11:35 a.out.core.22070    

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​

    ​檔案,關于此檔案的設定參看 這裡 。增加一行:

# /etc/security/limits.conf

#

#Each line describes a limit for a user in the form:

#<domain>   <type>   <item>   <value>

    *          soft     core   unlimited

  • 修改 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 檔案名。

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

9

10

11

int func(int *p)

    int y = *p;

    return y;

    int *p = NULL;

    return func(p);

編譯加上調試資訊, 運作之後core dump, 使用 gdb 檢視 core 檔案.

12

13

14

15

16

17

18

19

20

21

22

23

guohailin@guohailin:~$ gcc core_demo.c -o core_demo -g

guohailin@guohailin:~$ ./core_demo

guohailin@guohailin:~$ gdb core_demo core_demo.core.24816

...

Core was generated by './core_demo'.

Program terminated with signal 11, Segmentation fault.

#0  0x080483cd in func (p=0x0) at core_demo.c:5

5       int y = *p;

(gdb)  where

#1  0x080483ef in main () at core_demo.c:12

(gdb) info frame

Stack level 0, frame at 0xffd590a4:

eip = 0x80483cd in func (core_demo.c:5); saved eip 0x80483ef

called by frame at 0xffd590c0

source language c.

Arglist at 0xffd5909c, args: p=0x0

Locals at 0xffd5909c, Previous frame's sp is 0xffd590a4

Saved registers:

  ebp at 0xffd5909c, eip at 0xffd590a0

(gdb)

從上面可以看出,我們可以還原 core_demo 執行時的場景,并使用 ​

​where​

​ 可以檢視目前程式調用函數棧幀, 還可以使用 gdb 中的指令檢視寄存器,變量等資訊.

------------------------------------------------------------------------------------------------------------------------------------------------

指令如下:

gcc -g core.cpp

./a.out

gdb -c core a.out

gdb bt

繼續閱讀