當系統建立線程的時候,會為線程棧預訂一塊位址空間區域,并給該區域調撥一些實體存儲器。預設會預訂1MB的位址空間并調撥兩個頁面的存儲器。但是在建構 應用程式的時候可以改變這個預設值
在建構應用程式的時候連結器會把棧的大小寫入到exe和dll檔案的pe檔案頭中,當建立線程的時候會根據PE檔案頭中的大小來預訂空間區域。在調用CreateThread或_beginthreadex的時候開發人員可以指定需要在一開始就調撥的位址空間大小和存儲器大小。
下面顯示了一台頁面大小為4KB的機器上線程棧的位址空間區域(基址為0x08100000 )。該線程棧的位址空間區域和所調撥給該區域的都具有PAGE_READWRITE保護屬性。

在預訂位址空間後,系統會給區域頂部的兩個頁面調撥實體存儲器。線上程開始之前系統會把線程棧的指針指向最上面那兩個頁面的末尾。這個頁面就是線程開始使用棧的地方。區域頂部往下的第二個頁面被稱為防護頁面。随着調用的越來越多,調用樹也越來越深,線程也需要越來越多的棧空間
當線程試圖通路防護頁中的記憶體時,系統會得到通知這時系統會先給防護頁面下面的那個頁面調撥存儲器,接着去除目前防護頁面的PAGE_GUARD保護屬性标志,然後給剛調撥的存儲頁指定PAGE_GUARD保護屬性标志。該項技術使得系統能夠線上程需要的時候才增加棧存儲器大小。如果線程的調用樹 斷加深,那麼棧空間區域看起來會像下圖這樣
假設線程調用樹非常深,CPu的棧指針寄存器指向的記憶體位址為0x08003004現在當線程調用另一個函數時,系統必須調撥更多的實體存儲器。但是當系統給0x0800100的頁面調撥實體存儲器時,它的做法和給區域中的其他部分調撥實體存儲器有所不同。
由上圖可知,系統會除去0x08001000處的PAGE_GUARD保護屬性标志,然後給位址為0x08001000的頁面調撥實體存器。差別在于系統不會給剛調撥的實體存儲器0x09001000指定防護屬性。意味着棧的位址空間已經放滿了字所能容納得下的所有實體存儲器。系統永遠不會給區域底部的那個頁面調撥存儲器。
當系統給0x080000000調用實體存儲器時,它會執行一個額外的操作:抛出EXCEPTION_STACK_OVERFLOW異常。
可以用函數SetThreadStackGuarante函數來設定抛出前面講的EXCEPTION_STACK_OVERFLOW異常。
為什麼系統始終不給棧位址空間最底部的頁面調撥實體存儲器。這樣做目的是為了保護程序中其它資料。使他們不會因為意外的記憶體寫越界而遭到破壞。如果棧越過了所預訂的區域,那麼線程就會覆寫程序位址空間中其它資料。
另一種很難找到的缺陷是 棧下溢 看下面代碼
這段代碼試圖通路線程棧之外的記憶體。編譯器和連結器無法發現代碼中的此類錯誤。這條語句可能引發通路違規,也可能不會(如果通路的剛好是被調撥的話),發生這種情況程式可能會破壞程序另外的一部分記憶體,而系統是無法檢測到的。下面代碼中的棧一溢總會引起記憶體破壞,因為程式剛好線上程棧的後面配置設定了一塊記憶體。
16.1 C/C++運作庫的棧檢查函數
c++運作庫中有一個棧檢查函數。在編譯代碼時,編譯器會在必要時生成代碼來調用該函數。這個函數的目的是為了確定已經給線程棧調撥了實體存儲器。