當(dāng)程序運(yùn)行的過程中異常終止或崩潰,操作系統(tǒng)會將程序當(dāng)時的內(nèi)存狀態(tài)記錄下來,保存在一個文件中,這種行為就叫做Core Dump(中文有的翻譯成“核心轉(zhuǎn)儲”)。我們可以認(rèn)為 core dump 是“內(nèi)存快照”,但實際上,除了內(nèi)存信息之外,還有些關(guān)鍵的程序運(yùn)行狀態(tài)也會同時 dump 下來,例如寄存器信息(包括程序指針、棧指針等)、內(nèi)存管理信息、其他處理器和操作系統(tǒng)狀態(tài)和信息。core dump 對于編程人員診斷和調(diào)試程序是非常有幫助的,因為對于有些程序錯誤是很難重現(xiàn)的,例如指針異常,而 core dump 文件可以再現(xiàn)程序出錯時的情景。
Core Dump 名詞解釋
在半導(dǎo)體作為電腦內(nèi)存材料之前,電腦內(nèi)存使用的是?磁芯內(nèi)存(Magnetic Core Memory),Core Dump 中的 Core 沿用了磁芯內(nèi)存的?Core?表達(dá)。圖為磁芯內(nèi)存的一個單元,來自?Wikipedia.
在?APUE?一書中作者有句話這樣寫的:
Because the file is named?core, it shows how long this feature has been part of the Unix System.
這里的 core 就是沿用的是早期電腦磁芯內(nèi)存中的表達(dá),也能看出 Unix 系統(tǒng) Core Dump 機(jī)制的悠久歷史。
Dump?指的是拷貝一種存儲介質(zhì)中的部分內(nèi)容到另一個存儲介質(zhì),或者將內(nèi)容打印、顯示或者其它輸出設(shè)備。dump 出來的內(nèi)容是格式化的,可以使用一些工具來解析它。
現(xiàn)代操作系統(tǒng)中,用?Core Dump?表示當(dāng)程序異常終止或崩潰時,將進(jìn)程此時的內(nèi)存中的內(nèi)容拷貝到磁盤文件中存儲,以方便編程人員調(diào)試。
Core Dump 如何產(chǎn)生
上面說當(dāng)程序運(yùn)行過程中異常終止或崩潰時會發(fā)生 core dump,但還沒說到什么具體的情景程序會發(fā)生異常終止或崩潰,例如我們使用?kill -9?命令殺死一個進(jìn)程會發(fā)生 core dump 嗎?實驗證明是不能的,那么什么情況會產(chǎn)生呢?
Linux 中信號是一種異步事件處理的機(jī)制,每種信號對應(yīng)有其默認(rèn)的操作,你可以在?這里?查看 Linux 系統(tǒng)提供的信號以及默認(rèn)處理。默認(rèn)操作主要包括忽略該信號(Ingore)、暫停進(jìn)程(Stop)、終止進(jìn)程(Terminate)、終止并發(fā)生core dump(core)等。如果我們信號均是采用默認(rèn)操作,那么,以下列出幾種信號,它們在發(fā)生時會產(chǎn)生 core dump:
SignalActionCommentSIGQUITCoreQuit from keyboardSIGILLCoreIllegal InstructionSIGABRTCoreAbort signal from?abortSIGSEGVCoreInvalid memory referenceSIGTRAPCoreTrace/breakpoint trap
當(dāng)然不僅限于上面的幾種信號。這就是為什么我們使用?Ctrl+z?來掛起一個進(jìn)程或者?Ctrl+C?結(jié)束一個進(jìn)程均不會產(chǎn)生 core dump,因為前者會向進(jìn)程發(fā)出?SIGTSTP?信號,該信號的默認(rèn)操作為暫停進(jìn)程(Stop Process);后者會向進(jìn)程發(fā)出SIGINT?信號,該信號默認(rèn)操作為終止進(jìn)程(Terminate Process)。同樣上面提到的?kill -9?命令會發(fā)出?SIGKILL?命令,該命令默認(rèn)為終止進(jìn)程。而如果我們使用?Ctrl+?來終止一個進(jìn)程,會向進(jìn)程發(fā)出?SIGQUIT?信號,默認(rèn)是會產(chǎn)生 core dump 的。還有其它情景會產(chǎn)生 core dump, 如:程序調(diào)用?abort()?函數(shù)、訪存錯誤、非法指令等等。
下面舉兩個例子來說明:
終端下比較?Ctrl+C?和?Ctrl+:
guohailin@guohailin:~$ sleep 10 #使用sleep命令休眠 10 s^C #使用 Ctrl+C 終止該程序,不會產(chǎn)生 core dumpguohailin@guohailin:~$ sleep 10^Quit (core dumped) #使用 Ctrl+ 退出程序, 會產(chǎn)生 core dumpguohailin@guohailin:~$ ls #多出下面一個 core 文件-rw------- ?1 guohailin guohailin 335872 10月 22 11:31 sleep.core.21990
小程序產(chǎn)生 core dump
#include int main(){ int *null_ptr = NULL; *null_ptr = 10; //對空指針指向的內(nèi)存區(qū)域?qū)?會發(fā)生段錯誤 return 0;}
#編譯執(zhí)行g(shù)uohailin@guohailin:~$ ./a.outSegmentation fault (core dumped)guohailin@guohailin:~$ ls #多出下面一個 core 文件-rw------- 1 guohailin guohailin 200704 10月 22 11:35 a.out.core.22070
Linux 下打開 Core Dump
我使用的 Linux 發(fā)行版是 Ubuntu 13.04,設(shè)置生成 core dump 文件的方法如下:
打開 core dump 功能
在終端中輸入命令?ulimit -c?,輸出的結(jié)果為 0,說明默認(rèn)是關(guān)閉 core dump 的,即當(dāng)程序異常終止時,也不會生成 core dump 文件。
我們可以使用命令?ulimit -c unlimited?來開啟 core dump 功能,并且不限制 core dump 文件的大小; 如果需要限制文件的大小,將 unlimited 改成你想生成 core 文件最大的大小,注意單位為 blocks(KB)。
用上面命令只會對當(dāng)前的終端環(huán)境有效,如果想需要永久生效,可以修改文件?/etc/security/limits.conf文件,關(guān)于此文件的設(shè)置參看?這里?。增加一行:
# /etc/security/limits.conf##Each line describes a limit for a user in the form:## * soft core unlimited
修改 core 文件保存的路徑
默認(rèn)生成的 core 文件保存在可執(zhí)行文件所在的目錄下,文件名就為?core。
通過修改?/proc/sys/kernel/core_uses_pid?文件可以讓生成 core 文件名是否自動加上 pid 號。
例如?echo 1 > /proc/sys/kernel/core_uses_pid?,生成的 core 文件名將會變成?core.pid,其中 pid 表示該進(jìn)程的 PID。
還可以通過修改?/proc/sys/kernel/core_pattern?來控制生成 core 文件保存的位置以及文件名格式。
例如可以用?echo "/tmp/corefile-%e-%p-%t" > /proc/sys/kernel/core_pattern?設(shè)置生成的 core 文件保存在 “/tmp/corefile” 目錄下,文件名格式為 “core-命令名-pid-時間戳”。這里?有更多詳細(xì)的說明!
使用 gdb 調(diào)試 Core 文件
產(chǎn)生了 core 文件,我們該如何使用該 Core 文件進(jìn)行調(diào)試呢?Linux 中可以使用 GDB 來調(diào)試 core 文件,步驟如下:
首先,使用 gcc 編譯源文件,加上?-g?以增加調(diào)試信息;
按照上面打開 core dump 以使程序異常終止時能生成 core 文件;
運(yùn)行程序,當(dāng)core dump 之后,使用命令?gdb program core?來查看 core 文件,其中 program 為可執(zhí)行程序名,core 為生成的 core 文件名。
下面用一個簡單的例子來說明:
#include int func(int *p){ int y = *p; return y;}int main(){ int *p = NULL; return func(p);}
編譯加上調(diào)試信息, 運(yùn)行之后core dump, 使用 gdb 查看 core 文件.
guohailin@guohailin:~$ gcc core_demo.c -o core_demo -gguohailin@guohailin:~$ ./core_demo Segmentation fault (core dumped)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:55 int y = *p;(gdb) where#0 0x080483cd in func (p=0x0) at core_demo.c:5#1 0x080483ef in main () at core_demo.c:12(gdb) info frameStack 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 執(zhí)行時的場景,并使用?where?可以查看當(dāng)前程序調(diào)用函數(shù)棧幀, 還可以使用 gdb 中的命令查看寄存器,變量等信息.
?
評論
查看更多