一、前言:
有的程式可以通過編譯, 但在運行時會出現Segment fault(段錯誤). 這通常都是指標錯誤引起的. 但這不像編譯錯誤一樣會提示到檔案某一行, 而是沒有任何資訊, 使得我們的調試變得困難起來.
gdb: 有一種辦法是, 我們用gdb的step, 一步一步尋找. 這放在短小的代碼中是可行的, 但要讓你step一個上萬行的代碼, 我想你會從此厭惡程式員這個名字, 而把他叫做調試員. 我們還有更好的辦法, 這就是core file.
ulimit: 如果想讓系統在訊號中斷造成的錯誤時產生core檔案, 我們需要在shell中按如下設定: #設定core大小為無限 ulimit -c unlimited #設定檔案大小為無限 ulimit unlimited 這些需要有root許可權, 在ubuntu下每次重新開啟中斷都需要重新輸入上面的第一條命令, 來設定core大小為無限.
用gdb查看core檔案: 下面我們可以在發生運行時訊號引起的錯誤時發生core dump了. 發生core dump之後, 用gdb進行查看core檔案的內容, 以定位檔案中引發core dump的行. gdb [exec file] [core file] 如: gdb ./test test.core 在進入gdb後, 用bt命令查看backtrace以檢查發生程式運行到哪裡, 來定位core dump的檔案->行.
1. 什麼是Core:
Sam之前一直以為Core Dump中Core是 Linux Kernel的意思. 今天才發現在這裡,Core是另一種意思:
在使用半導體作為記憶體的材料前,人類是利用線圈當作記憶體的材料(發明者為王安),線圈就叫作 core ,用線圈做的記憶體就叫作 core memory。如今 ,半導體工業澎勃發展,已經沒有人用 core memory 了,不過,在許多情況下, 人們還是把記憶體叫作 core 。
2. 什麼是Core Dump:
我們在開發(或使用)一個程式時,最怕的就是程式莫明其妙地當掉。雖然系統沒事,但我們下次仍可能遇到相同的問題。於是這時作業系統就會把程式當掉 時的記憶體內容 dump 出來(現在通常是寫在一個叫 core 的 file 裡面),讓 我們或是 debugger 做為參考。這個動作就叫作 core dump。
3. Core Dump時會產生何種檔案:
Core Dump時,會產生諸如 core.進程號 的檔案。
4. 為何有時程式Down了,卻沒產生 Core檔案。
Linux下,有一些設定,標明了resources available to the shell and to processes。 可以使用#ulimit -a 來看這些設定。
從這裡可以看出,如果 -c是顯示:core file size (blocks, -c) 如果這個值為0,則無法產生core檔案。所以可以使用:
#ulimit -c 1024 或者 #ulimit -c unlimited 來使能 core檔案。
如果程式出錯時產生Core 檔案,則會顯示Segmentation fault (core dumped) 。例如:
#include <sys/resource.h>#include <sys/time.h>int set_coredump_size(void){int ret; struct rlimit rl;rl.rlim_cur = 2<<20; rl.rlim_max = 2<<20; ret = setrlimit(RLIMIT_CORE, &rl); if(ret < 0) { perror("setrlimit()"); } return ret;}int main(){char * ptr=0;set_coredump_size();while(1){*ptr=111;}return 0;}
5. Core Dump的核心轉儲檔案目錄和命名規則:
/proc/sys/kernel /core_uses_pid可以控制產生的core檔案的檔案名稱中是否添加pid作為擴充,如果添加則檔案內容為1,否則為0
6. 如何使用Core檔案:
在Linux下,使用:
#gdb -c core.pid program_name
就可以進入gdb模式。
輸入where,就可以指出是在哪一行被Down掉,哪個function內,由誰調用等等。
(gdb) where
或者輸入 bt。
(gdb) bt
7. 如何讓一個正常的程式down:
#kill -s SIGSEGV pid
8. 察看Core檔案輸出在何處:
存放Coredump的目錄即進程的目前的目錄,一般就是當初發出命令啟動該進程時所在的目錄。但如果是通過指令碼啟動,則指令碼可能會修改目前的目錄,這時進程真正的目前的目錄就會與當初執行指令碼所在目錄不同。這時可以查看”/proc/<進程pid>/cwd“符號連結的目標來確定進程真正的目前的目錄地址。通過系統服務啟動的進程也可通過這一方法查看。
二、Core Dump 配置與調試
1.core檔案的產生開關和大小限制
---------------------------------
1)使用ulimit -c 命令可查看core檔案的產生開關。若結果為0,則表示關閉了此功能,不會產生core檔案。
2)使用ulimit -c filesize命令,可以限制core檔案的大小(filesize的單位為kbyte)。若ulimit -c unlimited,則表示core檔案的大小不受限制。如果產生的資訊超過此大小,將會被裁剪,最終產生一個不完整的core檔案。在調試此core檔案的時候,gdb會提示錯誤。
2.core檔案的名稱和產生路徑
----------------------------
若系統產生的core檔案不帶其它任何副檔名稱,則全部命名為core。新的core檔案產生將覆蓋原來的core檔案 。
1)/proc/sys /kernel/core_uses_pid可以控制core檔案的檔案名稱中是否添加pid作為擴充。檔案內容為1,表示添加pid作為副檔名,產生的 core檔案格式為core.xxxx;為0則表示產生的core檔案同一命名為core。
可通過以下命令修改此檔案:
echo"1" >/proc/sys/kernel/core_uses_pid
2)proc/sys/kernel/core_pattern可以控制core檔案儲存位置和檔案名稱格式。
可通過以下命令修改此檔案:
echo"/corefile/core-%e-%p-%t" >core_pattern,可以將core檔案統一產生到/corefile目錄下,產生的檔案名稱為core-命令名-pid-時間戳記
以下是參數列表:
%p - insert pid into filename 添加pid
%u - insert current uid into filename 添加當前uid
%g - insert current gid into filename 添加當前gid
%s - insert signal that caused the coredump into the filename 添加導致產生core的訊號
%t - insert UNIX time that the coredump occurred into filename 添加core檔案產生時的unix時間
%h - insert hostname where the coredump happened into filename 添加主機名稱
%e - insert coredumping executable name into filename 添加命令名
3.用gdb查看core檔案:
下面我們可以在發生運行時訊號引起的錯誤時發生core dump了.
發生 core dump之後,用gdb進行查看core檔案的內容,以定位檔案中引發core dump的行.
gdb [exec file] [core file]
如:gdb ./test test.core
在進入gdb後,用 bt命令查看backtrace以檢查發生程式運行到哪裡,來定位core dump的檔案->行.
4.開發板上使用core檔案調試
-----------------------------
如果開發板的作業系統也是linux,core調試方法依然適用。如果開發板上不支援gdb,可將開發板的環境(標頭檔、庫)、可執行檔和core檔案拷貝到PC的linux下,運行相關命令即可。
注意:待調試的可執行檔,在編譯的時候需要加-g,core檔案才能正常顯示出錯資訊。
注意的問題:
在Linux下要保證程式崩潰時產生 Coredump要注意這些問題:
一、要保證存放Coredump的目錄存在且進程對該目錄有寫入權限。存放Coredump 的目錄即進程的目前的目錄,一般就是當初發出命令啟動該進程時所在的目錄。但如果是通過指令碼啟動,則指令碼可能會修改目前的目錄,這時進程真正的目前的目錄就會與當初執行指令碼所在目錄不同。這時可以查看”/proc/進程pid>/cwd“符號連結的目標來確定進程真正的目前的目錄地址。通過系統服務啟動的進程也可通過這一方法查看。
二、若程式調用了seteuid()/setegid()改變了進程的有效使用者或組,則在預設情況下系統不會為這些進程產生Coredump。很多服務程式都會調用seteuid(),如MySQL,不論你用什麼使用者運行 mysqld_safe啟動MySQL,mysqld進行的有效使用者始終是msyql使用者。如果你當初是以使用者A運行了某個程式,但在ps裡看到的
這個程式的使用者卻是B的話,那麼這些進程就是調用了seteuid了。為了能夠讓這些進程產生core dump,需要將/proc/sys/fs/suid_dumpable 檔案的內容改為1(一般預設是0)。
三、這個一般都知道,就是要設定足夠大的Core檔案大小限制了。程式崩潰時產生的 Core檔案大小即為程式運行時佔用的記憶體大小。但程式崩潰時的行為不可按平常時的行為來估計,比如緩衝區溢位等錯誤可能導致堆棧被破壞,因此經常會出現某個變數的值被修改成亂七八糟的,然後程式用這個大小去申請記憶體就可能導致程式比平常時多佔用很多記憶體。因此無論程式正常運行時佔用的記憶體多麼少,要保證產生Core檔案還是將大小限制設為unlimited為好。
ulimit -- 使用者資源限制命令
1、說明 :ulimit用於shell啟動進程所佔用的資源.
2、類別 :shell內建命令
3、文法格式 :ulimit [-acdfHlmnpsStvw] [size]
4、參數介紹 :
-H 設定硬體資源限制.
-S 設定軟體資源限制.
-a 顯示當前所有的資源限制.
-c size:設定core檔案的最大值.單位:blocks
-d size:設定資料區段的最大值.單位:kbytes
-f size:設定建立檔案的最大值.單位:blocks
-l size:設定在記憶體中鎖定進程的最大值.單位:kbytes
-m size:設定可以使用的常駐記憶體的最大值.單位:kbytes
-n size:設定核心可以同時開啟的檔案描述符的最大值.單位:n
-p size:設定管道緩衝區的最大值.單位:kbytes
-s size:設定堆棧的最大值.單位:kbytes
-t size:設定CPU使用時間的最大上限.單位:seconds
-v size:設定虛擬記憶體的最大值.單位:kbytes 5,簡單一實例:
5、舉例
在Linux下寫程式的時候,如果程式比較大,經常會遇到“段錯誤”(segmentationfault)這樣的問題,這主要就是由於Linux系統初始的堆棧大小(stack size)太小的緣故,一般為10M。我一般把stacksize設定成256M,這樣就沒有段錯誤了。命令為:ulimit -s 262140
如果要系統自動記住這個配置,就編輯/etc/profile檔案,在 “ulimit -S -c 0 > /dev/null 2>&1”行下,添加“ulimit -s 262140”,儲存重啟系統就可以了。
1] 在RH8的環境檔案/etc/profile中,我們可以看到系統是如何配置ulimit的:
#grep ulimit /etc/profile
ulimit -S -c 0 > /dev/null 2>&1
這條語句設定了對軟體資源和對core檔案大小的設定
2] 如果我們想要對由shell建立的檔案大小作些限制,如:
#ll h
-rw-r--r-- 1 lee lee 150062 7月 22 02:39 h
#ulimit -f 100 #設定建立檔案的最大塊(一塊=512位元組)
#cat h>newh
File size limit exceeded
#ll newh
-rw-r--r-- 1 lee lee 51200 11月 8 11:47 newh
檔案h的大小是150062位元組,而我們設定的建立檔案的大小是512位元組x100塊=51200位元組,當然系統就會根據你的設定產生了51200位元組的newh檔案.
3] 可以像執行個體1]一樣,把你要設定的ulimit放在/etc/profile這個環境檔案中.
用途:設定或報告使用者資源極限。
文法:ulimit [ -H ] [ -S ] [ -a ] [ -c ] [ -d ] [ -f ] [ -m ] [ -n ] [ -s ] [ -t ] [ Limit ]
描述:ulimit 命令設定或報告使用者進程資源極限,如 /etc/security/limits 檔案所定義。檔案包含以下預設值極限:
fsize = 2097151
core = 2097151
cpu = -1
data = 262144
rss = 65536
stack = 65536
nofiles = 2000
當新使用者添加到系統中時,這些值被作為預設值使用。當向系統中添加使用者時,以上值通過 mkuser 命令設定,或通過 chuser 命令更改。
極限分為軟性或硬性。通過 ulimit 命令,使用者可將軟極限更改到硬極限的最大設定值。要更改資源硬極限,必須擁有 root 使用者權限。
很多系統不包括以上一種或數種極限。 特定資源的極限在指定 Limit 參數時設定。Limit 參數的值可以是每個資源中指定單元中的數字,或者為值 unlimited。要將特定的 ulimit 設定為 unlimited,可使用詞 unlimited。
註:在 /etc/security/limits 檔案中設定預設極限就是設定了系統寬度極限, 而不僅僅是建立使用者時使用者所需的極限。
省略 Limit 參數時,將會列印出當前資源極限。除非使用者指定 -H 標誌,否則列印出軟極限。當使用者指定一個以上資源時,極限名稱和單元在值之前列印。如果未給予選項,則假定帶有了 -f 標誌。
由於 ulimit 命令影響當前 shell 環境,所以它將作為 shell 常規內建命令提供。如果在獨立的命令執行環境中調用該命令,則不影響調用者環境的檔案大小極限。以下樣本中正是這種情況:
nohup ulimit -f 10000
env ulimit 10000
一旦通過進程減少了硬極限,若無 root 特權則無法增加,即使返回到原值也不可能。
關於使用者和系統資源極限的更多資訊,請參見 AIX 5L Version 5.3 Technical Reference: BaseOperating System and Extensions Volume 1 中的 getrlimit、setrlimit 或vlimit 子常式。
標誌
-a 列出所有當前資源極限。
-c 以 512 位元組塊為單位,指定核心轉儲的大小。
-d 以 K 位元組為單位指定資料區域的大小。
-f 使用 Limit 參數時設定檔案大小極限(以塊計),或者在未指定參數時報告檔案大小極限。預設值為 -f 標誌。
-H 指定設定某個給定資源的硬極限。如果使用者擁有 root 使用者權限,可以增大硬極限。任何使用者均可減少硬極限。
-m 以 K 位元組為單位指定實體儲存體器的大小。
-n 指定一個進程可以擁有的檔案描述符的數量的極限。
-s 以 K 位元組為單位指定堆棧的大小。
-S 指定為給定的資源設定軟極限。軟極限可增大到硬極限的值。如果 -H 和 -S 標誌均未指定,極限適用於以上二者。
-t 指定每個進程所使用的秒數 。
退出狀態
返回以下退出值:
0 成功完成。