當(dāng)前位置:首頁 > 公眾號精選 > 嵌入式大雜燴
[導(dǎo)讀]一旦一個程序發(fā)生了越界訪問,cpu 就會產(chǎn)生相應(yīng)的保護(hù),于是 segmentation fault 就出現(xiàn)了,通過上面的解釋,段錯誤應(yīng)該就是訪問了不可訪問的內(nèi)存。這個內(nèi)存區(qū)要么是不存在的,要么是受到系統(tǒng)保護(hù)的,還有可能是缺少文件或者文件損壞。

原文:
https://blog.csdn.net/qq_29350001/article/details/53780697

一、什么是段錯誤?

一旦一個程序發(fā)生了越界訪問,cpu 就會產(chǎn)生相應(yīng)的保護(hù),于是 segmentation fault 就出現(xiàn)了,通過上面的解釋,段錯誤應(yīng)該就是訪問了不可訪問的內(nèi)存

這個內(nèi)存區(qū)要么是不存在的,要么是受到系統(tǒng)保護(hù)的,還有可能是缺少文件或者文件損壞。

二、段錯誤產(chǎn)生的原因

下面是一些典型的段錯誤的原因:

  • 非關(guān)聯(lián)化空指針——這是特殊情況由內(nèi)存管理硬件
  • 試圖訪問一個不存在的內(nèi)存地址(在進(jìn)程的地址空間)
  • 試圖訪問內(nèi)存的程序沒有權(quán)利(如內(nèi)核結(jié)構(gòu)流程上下文)
  • 試圖寫入只讀存儲器(如代碼段)

1、訪問不存在的內(nèi)存地址

在C代碼,分割錯誤通常發(fā)生由于指針的錯誤使用,特別是在C動態(tài)內(nèi)存分配。非關(guān)聯(lián)化一個空指針總是導(dǎo)致段錯誤。

但野指針和懸空指針指向的內(nèi)存,可能會或可能不會存在,而且可能或不可能是可讀的還是可寫的,因此會導(dǎo)致瞬態(tài)錯誤。

#include?

int?main?(void)
{
?int?*ptr?=?NULL;
?*ptr?=?0;
?return?0;
}
輸出結(jié)果:
段錯誤(核心已轉(zhuǎn)儲)

現(xiàn)在,非關(guān)聯(lián)化這些變量可能導(dǎo)致段錯誤:非關(guān)聯(lián)化空指針通常會導(dǎo)致段錯誤,閱讀時從野指針可能導(dǎo)致隨機(jī)數(shù)據(jù)但沒有段錯誤,和閱讀從懸空指針可能導(dǎo)致有效數(shù)據(jù),然后隨機(jī)數(shù)據(jù)覆蓋。

2、訪問系統(tǒng)保護(hù)的內(nèi)存地址

#include?
?
int?main?(void)
{
?int?*ptr?=?(int?*)0;
?*ptr?=?100;
?return?0;
}
輸出結(jié)果:
段錯誤(核心已轉(zhuǎn)儲)

3、訪問只讀的內(nèi)存地址

寫入只讀存儲器提出了一個 segmentation fault,這個發(fā)生在程序?qū)懭胱约旱囊徊糠执a段或者是只讀的數(shù)據(jù)段,這些都是由操作系統(tǒng)加載到只讀存儲器。

#include?
#include?
?
int?main?(void)
{
?char?*ptr?=?"test";
?strcpy?(ptr,?"TEST");
?return?0;
}
輸出結(jié)果:
段錯誤(核心已轉(zhuǎn)儲)
#include?
?
int?main?(void)
{
?char?*ptr?=?"hello";
?*ptr?=?'H';
?return?0;
}
輸出結(jié)果:
段錯誤(核心已轉(zhuǎn)儲)


上述例子ANSI C代碼通常會導(dǎo)致段錯誤和內(nèi)存保護(hù)平臺。它試圖修改一個字符串文字,這是根據(jù)ANSI C標(biāo)準(zhǔn)未定義的行為。大多數(shù)編譯器在編譯時不會抓,而是編譯這個可執(zhí)行代碼,將崩潰。

包含這個代碼被編譯程序時,字符串“hello”位于rodata部分程序的可執(zhí)行文件的只讀部分?jǐn)?shù)據(jù)段。

當(dāng)加載時,操作系統(tǒng)與其他字符串和地方常數(shù)只讀段的內(nèi)存中的數(shù)據(jù)。當(dāng)執(zhí)行時,一個變量 ptr 設(shè)置為指向字符串的位置,并試圖編寫一個H字符通過變量進(jìn)入內(nèi)存,導(dǎo)致段錯誤。

編譯程序的編譯器不檢查作業(yè)的只讀的位置在編譯時,和運行類unix操作系統(tǒng)產(chǎn)生以下運行時發(fā)生 segmentation fault。

可以糾正這個代碼使用一個數(shù)組而不是一個字符指針,這個棧上分配內(nèi)存并初始化字符串的值:

#include?
?
int?main?(void)
{
?char?ptr[]?=?"hello";
?ptr[0]?=?'H';
?return?0;
}


即使不能修改字符串(相反,這在C標(biāo)準(zhǔn)未定義行為,在C char *類型,所以沒有隱式轉(zhuǎn)換原始代碼,在c++的 const char *類型,因此有一個隱式轉(zhuǎn)換,所以編譯器通常會抓住這個特定的錯誤。

4、空指針廢棄

因為是一個很常見的程序錯誤空指針廢棄(讀或?qū)懺谝粋€空指針,用于C的意思是“沒有對象指針”作為一個錯誤指示器),大多數(shù)操作系統(tǒng)內(nèi)存訪問空指針的地址,這樣它會導(dǎo)致段錯誤。

#include?
?
int?main?(void)
{
?int?*ptr?=?NULL;
?printf?("%d\n",?*ptr);
?return?0;
}
輸出結(jié)果:
段錯誤(核心已轉(zhuǎn)儲)


這個示例代碼創(chuàng)建了一個空指針,然后試圖訪問它的值(讀值)。在運行時在許多操作系統(tǒng)中,這樣做會導(dǎo)致段錯誤。

非關(guān)聯(lián)化一個空指針,然后分配(寫一個值到一個不存在的目標(biāo))也通常會導(dǎo)致段錯誤。

#include?
?
int?main?(void)
{
?int?*ptr?=?NULL;
?*ptr?=?1;
?return?0;
}
輸出結(jié)果:
段錯誤(核心已轉(zhuǎn)儲)


下面的代碼包含一個空指針,但當(dāng)編譯通常不會導(dǎo)致段錯誤,值是未使用的。因此,廢棄通常會被優(yōu)化掉,死代碼消除。

#include?
?
int?main?(void)
{
?int?*ptr?=?NULL;
?*ptr;
?return?0;
}


還有,比如malloc 動態(tài)分配內(nèi)存,釋放、置空完成后,不可再使用該指針。

#include?
#include?
#include?
?
int?main()
{
?char*?str=(char*?)malloc(100);
?if(*str)
?{
??return;?
?}
?strcpy(str,"hello");
?printf("%s\n",str);
?free(str);
?str=NULL;
?strcpy(str,"abcdef");
?return?0;
}
輸出結(jié)果:
hello
段錯誤?(核心已轉(zhuǎn)儲)

5、堆棧溢出

#include?
#include?
?
int?main?(void)
{
?main?();
?return?0;
}
輸出結(jié)果:
段錯誤(核心已轉(zhuǎn)儲)


上述例子的無限遞歸,導(dǎo)致的堆棧溢出會導(dǎo)致段錯誤,但無線遞歸未必導(dǎo)致堆棧溢出,優(yōu)化執(zhí)行的編譯器和代碼的確切結(jié)構(gòu)。在這種情況下,遙不可及的代碼(返回語句)行為是未定義的。

因此,編譯器可以消除它,使用尾部調(diào)用優(yōu)化,可能導(dǎo)致沒有堆棧使用。其他優(yōu)化可能包括將遞歸轉(zhuǎn)換成迭代,給出例子的結(jié)構(gòu)功能永遠(yuǎn)會導(dǎo)致程序運行,雖然可能不是其他堆棧溢出。

6、內(nèi)存越界(數(shù)組越界,變量類型不一致等)

#include?

int?main?(void)
{
?char?test[10];
?printf?("%c\n",?test[100000]);
?return?0;
}
輸出結(jié)果:
段錯誤(核心已轉(zhuǎn)儲)



猜你喜歡

bug解決不了?使用日志法

從單片機(jī)工程師的角度看嵌入式Linux

Linux下應(yīng)用開發(fā)基礎(chǔ)

【Linux筆記】設(shè)備樹實例分析


免責(zé)聲明:本文來源網(wǎng)絡(luò),免費傳達(dá)知識,版權(quán)歸原作者所有。如涉及作品版權(quán)問題,請聯(lián)系我進(jìn)行刪除。


免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
關(guān)閉
關(guān)閉