棧空間 棧又溢位了

2021-10-16 06:40:25 字數 3351 閱讀 8528

執行程式後,異常發生了。對於程式崩潰,早就見怪不怪了。重啟程式,附加偵錯程式,再次執行相同的功能,果然中斷到偵錯程式中。有了上次的經驗(沒仔細看錯誤提示導致懵逼了很久,文章在這裡),仔細檢查了錯誤碼,又是0xc00000fd, stackoverflow。在vs2013中按ctrl + alt + c檢視呼叫棧,發現呼叫棧並不深,沒有遞迴呼叫的跡象。仔細看報錯的位置,居然沒有執行到任何**。下圖是我用測試**截的圖:

函式呼叫的時候,會把引數、區域性變數、返回位址等資訊都儲存在棧上,而棧空間預設只有1 mb,如果呼叫棧幀太多,那麼可能會用光這1 mb,從而導致stack overflow

小貼士:這裡的1 mb不太精確,實際可用的棧空間比1 mb小,最後乙個頁面永遠是不可用的。為了描述簡單而且好記就這麼描述了。
呼叫棧並不深,難道就是這幾個棧幀就把棧耗光了?簡單瀏覽當前函式中的區域性變數和引數,很快就找到了幾個值得懷疑的區域性變數。但是通過sizeof檢視對應結構體大小後發現:雖然大(大概400 kb),但是並沒有大到爆棧的程度。繼續觀察,發現了乙個很有意思的現象,這些變數在每個if else分支中都定義了乙份,難道這些分支中的區域性變數占用的棧空間被累加了?乙個大概400 kb3個加起來就超過1 mb了(預設的棧大小是1 mb),足以爆棧了!到底是不是這樣的呢?

為了確認猜想是否正確,新建乙個簡單的測試工程,測試**如下:

#include "stdafx.h"

struct bigdata ;

void use(bigdata* pdata)

void corrpuptstackeasyly(int argc)

else if (argc == 3)

else

}int _tmain(int argc, _tchar* ar**)

bigdata大概占用400 kb,如果猜想(三個bigdata型別的區域性變數會占用1.2 mb左右的空間)是正確的,那麼這個函式應該會爆棧。編譯執行,果真和猜想的一樣——爆棧了!

檢視函式corrpuptstackeasyly對應的反彙編,如下圖:

0x12c0dc轉換成十進位制是1229020大概是1.2 mb__alloca_probe是編譯器生成的函式,內部直接跳轉到_chkstk

從名字可以很容易的猜出_chkstk是用來檢查棧的。當函式中包含超大區域性變數(大於等於乙個頁面,4 kb)時,編譯器會在函式頭部插入一段檢查棧是否夠用的**。

_chkstk雖然是彙編**寫的,但是內部邏輯並不複雜,而且在安裝vs的時候提供了帶注釋的原始碼,可讀性極強。我機器上同時安裝了vs2010vs2013,可以在下圖中的幾個位置找到_chkstk對應的彙編**檔案chkstk.asm,如下圖:

因為這個函式超級精煉,有效彙編**不到20行,這裡截圖放上來,感興趣的小夥伴兒可以讀一讀:

稍微解釋幾個關鍵點:

eax記錄了需要檢查的棧大小,外部呼叫的時候需要設定好。

ecx記錄最低位址(棧是從高向低擴充套件的)。(73,74 行)

根據esp計算出當前位址所屬頁面的起始位置。(83,84 行)

判斷是否結束,沒結束則執行 5,6,7步。(87, 88 行)

讀取四位元組(99行)。

本行**是關鍵,如果訪問的位址所在的頁面是保護頁面(帶有page_guard屬性)並且經判定不需要拋棧溢位異常,則會觸發status_guard_page_violation異常(應該內部叫_xcpt_guard_page_violation,異常碼是0x80000001),作業系統會去除保護頁面的保護屬性,並分配物理記憶體,為下乙個介面設定保護屬性。

跳轉到第四步(cs10的位置),不斷重複這個過程。(100行)

注意:建立執行緒的時候指定了乙個棧保留大小(預設是1mb),剛開始的時候這1mb並不是都對應著物理記憶體,是按需分配的。這裡說的增長棧空間,並不是棧保留大小變大了,而是占用的物理頁增多了。相信大多數小夥伴兒應該已經知道了,但是這裡還是要囉嗦一句:訪問某個虛擬位址的時候,只有當這個虛擬位址對應的頁面有與之對應的物理頁面才可以訪問,否則會報訪問異常。
知道問題的根源後,解決就簡單了。只需要消除重複的大區域性變數即可。把分支中重複的變數提取到函式開始的位置即可。

void corrpuptstackeasyly(int argc)

else if (argc == 3)

else

}

說實話,解決完這個問題後,我是震驚的!vs真的就這麼簡單粗暴的把所有區域性變數的大小累加起來為函式分配棧空間嗎?這太太太不合理了!如果真是這樣,分支多的函式太有可能出現棧溢位了。個人覺得合理的做法是:把分支中占用記憶體最大的作為分支部分的記憶體占用,加上其它不在分支中的區域性變數的記憶體空間來為函式分配棧空間。

棧又溢位了

執行程式後,異常發生了。對於程式崩潰,早就見怪不怪了。重啟程式,附加偵錯程式,再次執行相同的功能,果然中斷到偵錯程式中。有了上次的經驗 沒仔細看錯誤提示導致懵逼了很久,文章在這裡 仔細檢查了錯誤碼,又是0xc00000fd,stackoverflow。在vs2013中按ctrl alt c檢視呼叫棧...

棧空間溢位

嵌入式project師們免不了和堆疊打交道,深入理解了這兩個東西,才幹從 編寫時就考慮清楚記憶體管理。避免到後期出現各種莫名其妙的問題。近期在使用cc2530時也遇到了一些問題,懷疑跟棧空間溢位有關,於是做了一次梳理。可能有些理解還不到位,等了解了再修正。所謂棧空間,就是一塊記憶體空間。而溢位,就是...

棧空間溢位

嵌入式project師們免不了和堆疊打交道,深入理解了這兩個東西,才幹從 編寫時就考慮清楚記憶體管理。避免到後期出現各種莫名其妙的問題。近期在使用cc2530時也遇到了一些問題,懷疑跟棧空間溢位有關,於是做了一次梳理。可能有些理解還不到位,等了解了再修正。所謂棧空間,就是一塊記憶體空間。而溢位,就是...