產生segment fault的原因及調試方法總結

段錯誤(以下定義摘自C專家編程)

段錯誤通常是由於解除引用一個未初始化或非法值的指針引起的。以發生頻率爲序,最終可能導致段錯誤的常見編程錯誤是:

1壞指針錯誤:在指針賦值之前就用它來引用內存;或者向庫函數傳遞一個壞指針(如果調試器顯示系統程序中出現了段錯誤,很可能並不是系統程序引起的段錯誤,問題可能就出現在自己的代碼中);或者指針被釋放後還繼續訪問它的內容。

2改寫錯誤:越過數組邊界寫入數據,在動態分配的內存空間以外寫入數據,或改寫一些堆管理數據結構(在動態分配的內存之前的區域寫入數據就很容易發生這種情況)

3指針釋放引起的錯誤:釋放同一塊內存兩次,或釋放一塊未曾使用malloc分類的內存,或釋放一個無效的指針。一個極爲常見的與釋放內存有關的錯誤就是在 for(p=start;p;p=p->next) 這樣的循環中迭代一個鏈表,並在循環體內使用 free(p) 這樣的語句。這樣,在下一次循環迭代時,程序就會對已經釋放的指針進行解除引用操作,從而導致不可預料的結果。 


問: 以下這段C++代碼會有問題嗎?有幾個printf能正常輸出呢?

#include <stdio.h>
#include <stdlib.h>


class Data {
  public :
    void test_data() {
      printf("test_dtata\n");  // printf2
      printf("test_dtata:%d\n", data_); //printf3
    }
  private :
    int data_;
};


class Container {
  public :
    void test_container() {
      printf("test_container\n");  // printf1
      data_.test_data();
    }
  private :
    Data data_;
};


int main()
{
  Container *c = NULL;
  c->test_container();
  return 0;
}

答案是有兩個可以輸出,第三個會引起segment fault!

test_container
test_dtata
段錯誤


問: 爲什麼第一第二個printf能輸出,第三個不行?

因爲其實  c->test_container();最終等價於 test_container(NULL);然後調用test_data(NULL) ,結果在

  printf("test_dtata:%d\n", data_); //printf3  訪問NULL->data_,顯然這是非法訪問,所以出錯。


問:爲什麼我要舉這個例子?

因爲printf能輸出,直覺就是指針不可能是空的,否則調用不了函數。

我在調試的時候遇到segment fault,具體的項目比這個小例子複雜的多,

一開始是以爲線程同步沒處理好多次釋放了內存導致segment fault,最後發現是未賦值,僅初始化爲NULL)。


如何調試segment fault:

1、 gdb 直接調試,用bt來看segment fault的棧情況

2、 對已產生 core dump的,用gdb program.exe core調試,同樣用bt來看,默認不產生coredump,如何開啓見:http://blog.csdn.net/huangkq1989/article/details/7032776

3、 通過dmesg來看:【引用於: http://zengji.li.blog.163.com/blog/static/87078186201011154156409/】

 kernel : *** : segfault at 0000000000000011 rip 00000032f8670454 rsp 00
0000004128fd30 error 4

這種信息一般都是由內存訪問越界造成的,不管是用戶態程序還是內核態程序訪問越界都會出core, 並在系統日誌裏面輸出一條這樣的信息。這條信息的前面分別是訪問越界的程序名,進程ID號,訪問越界的地址以及當時進程堆棧地址等信息,比較有用的信息是 最後的error number. 在上面的信息中,error number4 ,下面詳細介紹一下error number的信息:
在上面的例子中,error number6, 轉成二進制就是110, bit2=1, bit1=1, bit0=0, 按照上面的解釋,我們可以得出這條信息是由於用戶態程序讀操作訪問越界造成的。
error number是由三個字位組成的,從高到底分別爲bit2 bit1bit0,所以它的取值範圍是0~7.

bit2: 值爲1表示是用戶態程序內存訪問越界,值爲0表示是內核態程序內存訪問越界
bit1: 值爲1表示是寫操作導致內存訪問越界,值爲0表示是讀操作導致內存訪問越界
bit0: 值爲1表示沒有足夠的權限訪問非法地址的內容,值爲0表示訪問的非法地址根本沒有對應的頁面,也就是無效地址


然後用addr2line -e testseg 0000000000400470 -f可得到出問題的函數及對應的行。





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