Segment fault的一種處理方法
最近寫程式的時候,碰到了segment fault,花了兩天時間找出瞭解決辦法。下面是我看到比較好的方法。
有的程式可以通過編譯,但在執行時會出現Segment fault(段錯誤)。這通常都是指標錯誤引起的。但這不像編譯錯誤一樣會提示到檔案一行,而是沒有任何資訊。一種辦法是用gdb的step, 一步一步尋找。但要step一個上萬行的程式碼讓人難以想象。 我們還有更好的辦法,這就是core file。
如果想讓系統在訊號中斷造成的錯誤時產生core檔案, 我們需要在shell中按如下設定:
#設定core大小為無限 ulimit -c unlimited
#設定檔案大小為無限 ulimit unlimited
發生core dump之後,用gdb進行檢視core檔案的內容, 以定位檔案中引發core dump的行:
gdb [exec file] [core file]
如: gdb ./test test.core 在進入gdb後, 用bt命令檢視backtrace以檢查發生程式執行到哪裡,來定位core dump的檔案->行。
另外需要注意的是,如果你的機器上跑很多的應用,你生成的core又不知道是哪個應用產生的,你可以通過下列命令進行檢視:file core
幾個問題:
1. 什麼是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 來看這些設定。 (ulimit是bash built-in Command)
從這裡可以看出,如果 -c是顯示:core file size。如果這個值為0,則無法生成core檔案。所以可以使用:#ulimit -c 1024 或者 #ulimit -c unlimited 來使能 core檔案。如果程式出錯時生成Core 檔案,則會顯示Segmentation fault (core dumped) 。
5. Core Dump的核心轉儲檔案目錄和命名規則:
/proc/sys/kernel /core_uses_pid可以控制產生的core檔案的檔名中是否新增pid作為擴充套件,如果新增則檔案內容為1,否則為0
可通過以下命令修改此檔案:
echo "1" > /proc/sys/kernel/core_uses_pid
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“符號連結的目標來確定程序真正的當前目錄地址。通過系統服務啟動的程序也可通過這一方法檢視。
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 新增命令名
在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為好。
四、異常退出就一定會生成core嗎? 難道沒有不生成core的異常退出?
如果不是正常退出的那就是有訊號引起的程式退出,有些訊號確實能引起程式退出但不生成core。
SIGHUP 終止程序 終端線路結束通話
SIGINT 終止程序 中斷程序
SIGQUIT 建立CORE檔案終止程序,並且生成core檔案
SIGILL 建立CORE檔案 非法指令
SIGTRAP 建立CORE檔案 跟蹤自陷
SIGBUS 建立CORE檔案 匯流排錯誤
SIGSEGV 建立CORE檔案 段非法錯誤
SIGFPE 建立CORE檔案 浮點異常
SIGIOT 建立CORE檔案 執行I/O自陷
SIGKILL 終止程序 殺死程序
SIGPIPE 終止程序 向一個沒有讀程序的管道寫資料
SIGALARM 終止程序 計時器到時
SIGTERM 終止程序 軟體終止訊號
SIGSTOP 停止程序 非終端來的停止訊號
SIGTSTP 停止程序 終端來的停止訊號
SIGCONT 忽略訊號 繼續執行一個停止的程序
SIGURG 忽略訊號 I/O緊急訊號
SIGIO 忽略訊號 描述符上可以進行I/O
SIGCHLD 忽略訊號 當子程序停止或退出時通知父程序
SIGTTOU 停止程序 後臺程序寫終端
SIGTTIN 停止程序 後臺程序讀終端
SIGXGPU 終止程序 CPU時限超時
SIGXFSZ 終止程序 檔案長度過長
SIGWINCH 忽略訊號 視窗大小發生變化
SIGPROF 終止程序 統計分佈圖用計時器到時
SIGUSR1 終止程序 使用者定義訊號1
SIGUSR2 終止程序 使用者定義訊號2
SIGVTALRM 終止程序 虛擬計時器到
把可能的訊號都設定上控制代碼,看是那種情況。