段錯誤?打的就是段錯誤!!

在這裏插入圖片描述


呵,段錯誤?自從我看了這篇文章,我還會怕你個小小段錯誤?
請打開你的Linux終端,跟緊咯,準備發車!!嘟嘟嘟噠~~

①一段有段錯誤的代碼

#include<stdio.h>

void errfunc()
{
	int *p = NULL;
    *p = 1;
}

int main()
{
    errfunc();
    return 0;
}

這段代碼拿去運行,肯定段錯誤。

②跟我一起 -> 直達病竈

系統會在程序崩潰的那一剎那將整個內核的信息記錄在一個文件裏邊。如果你是第一次,那麼ls是查不到的。

這樣:使用命令 ulimit -a 打開ulimit這個文件。會看到如下:
在這裏插入圖片描述

發現這個文件大小缺省爲0,文件根本就裝不進去,那麼就需要我們手動將這個文件的大小改大一點:ulimit -c 10240,當然,可以設置別的大小,看你心情。

然後再運行一次段錯誤的文件,然後去ls,就會發現一個core.xxxx的文件,這裏提醒一下,最好先ls,看看系統下有沒有已存在的core文件,省的到時候不知道是哪個。

接下來有一步可走可不走的:可以使用命令 file core.4377(我測試的號碼是這個) ,將core.4377這個文件的具體信息給顯示出來,命令最後會顯示這個core文件是通過哪個文件產生的。

最後一步:gdb調試。這個執行文件叫dcw,是我的。gdb dcw core.4377,就會看到如下:
在這裏插入圖片描述

在最後,它會告訴你,在那個函數、那個地址出了問題。有些比較高級的gdb甚至會告訴你是哪一行!!可惜我的gdb就不說。。。

哈哈,開個玩笑。
注意:調段錯誤,編譯的時候一定要加入-g選項,要不然在最後顯示錯誤的時候只會顯示錯的地址,而不會顯示錯誤的具體信息

最後退出gdb調試:q,回車

③看我對症下藥

段錯誤的原因無非是內存越界,據不完全統計,主要有以下這些情況:


1 使用非法的內存地址(指針),包括使用未經初始化及已經釋放的指針、不存在的地址、受系統保護的地址,只讀的地址等,這一類也是最常見和最好解決的段錯誤問題,使用GDB print一下即可知道原因。

2 內存讀/寫越界。包括數組訪問越界,或在使用一些寫內存的函數時,長度指定不正確或者這些函數本身不能指定長度,典型的函數有strcpy(strncpy)sprintf(snprint)等等。

3 對於C++對象,應該通過相應類的接口來去內存進行操作,禁止通過其返回的指針對內存進行寫操作,典型的如string類的c_str()接口,如果你強制往其返回的指針進行寫操作肯定會段錯誤的,因爲其返回的地址是隻讀的。

4 函數不要返回其中局部對象的引用或地址,當函數返回時,函數棧彈出,局部對象的地址將失效,改寫或讀這些地址都會造成未知的後果。

5 避免在棧中定義過大的數組,否則可能導致進程的棧空間不足,此時也會出現段錯誤,同樣的,在創建進程/線程時如果不知道此線程/進程最大需要多少棧空間時最好不要在代碼中指定棧大小,應該使用系統默認的,這樣問題比較好查,ulimit一下即可知道。這類問題也是爲什麼我的程序在其他平臺跑得好好的,爲什麼一移植到這個平臺就段錯誤了。

6 操作系統的相關限制,如:進程可以分配的最大內存,進程可以打開的最大文件描述符個數等,在Linux下這些需要通過ulimit、setrlimit、sysctl等來解除相關的限制,這類段錯誤問題在系統移植中也經常發現,以前我們移植Linux的程序到VxWorks下時經常遇到(VxWorks要改內核配置來解決)。

7 多線程的程序,涉及到多個線程同時操作一塊內存時必須進行互斥,否則內存中的內容將不可預料。

8 在多線程環境下使用非線程安全的函數調用,例如 strerror 函數等。

9 在有信號的環境中,使用不可重入函數調用,而這些函數內部會讀或寫某片內存區,當信號中斷時,內存寫操作將被打斷,而下次進入時將無法避免地出錯。

10 跨進程傳遞某個地址,傳遞的都是經過映射的虛擬地址,對另外一個進程是不通用的。

11 某些有特殊要求的系統調用,例如epool_wait,正常情況下使用close關閉一個套接字後,epool會不再返回這個socket上的事件,但是如果你使用dup或dup2操作,將導致epool無法進行移除操作,此時再進行讀寫操作肯定是段錯誤的。

加油啦,一定是可以實現的啦!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章