2013-03-11 232 views
2

我正在編寫一個使用x86指令集的程序。當我使用存儲在大小爲40kb的堆棧中的本地數組時,它爲什麼會崩潰。使用帶有i5處理器的Windows7操作系統,在Visual C++編譯++速成版2008堆棧大小的最大使用限制是否有限制?

+0

那麼對堆棧一個本地陣列是不是一個漂亮的事情,如果你想成爲一個男人店呢其他地方,二來您的問題提供一些額外的信息(平臺最重要的)和三個沒有默認的限制,但有是幾種技術來確定你有多少內存。 – Pyjong 2013-03-11 10:05:10

+0

IIRC大多數VC版本默認使用2MB堆棧大小。 – 2013-03-11 10:13:03

回答

8

我想你擊中的保護頁的形式捕捉

IM。爲了不浪費實際內存直到實際使用,Windows最初保留了完整的堆棧空間(默認爲1MB;可以通過編輯PE頭文件來更改),但是隻提交兩頁,並且使第二個頁面成爲警戒頁面。保護頁面是一個頁面(4KB)的內存,它在對其進行訪問時會觸發一個特殊的異常(STATUS_GUARD_PAGE_VIOLATION)。當內核檢測到一個保護頁面異常時,它會提交所觸及的頁面並在其後面添加另一個保護頁面。這樣,如果你的函數在堆棧中推送小變量,它會「自行」增長。

然而,有一個問題,如果你嘗試分配一個本地變量,它是在4K(4096個字節)的大小。通常情況下,堆棧分配是通過簡單地從ESP中減去完成的。如果您從中減去超過4K,然後嘗試寫入堆棧,則有可能會在防護頁上進行拍攝,並在其後訪問保留的內存。這個不會被內核捕獲,但會傳遞給你的程序,並且通常會導致崩潰。

的解決方案是簡單的 - 做堆疊在4K的塊分配(= 4096 = 0×1000字節)和接觸紙堆每一個後觸發保護頁。 MSVC編譯器通過在使用超過4K局部變量的函數開始時調用__chkstk()函數自動執行此操作。這裏的功能從CRT源列表:

;*** 
;_chkstk - check stack upon procedure entry 
; 
;Purpose: 
;  Provide stack checking on procedure entry. Method is to simply probe 
;  each page of memory required for the stack in descending order. This 
;  causes the necessary pages of memory to be allocated via the guard 
;  page scheme, if possible. In the event of failure, the OS raises the 
;  _XCPT_UNABLE_TO_GROW_STACK exception. 
; 
;  NOTE: Currently, the (EAX < _PAGESIZE_) code path falls through 
;  to the "lastpage" label of the (EAX >= _PAGESIZE_) code path. This 
;  is small; a minor speed optimization would be to special case 
;  this up top. This would avoid the painful save/restore of 
;  ecx and would shorten the code path by 4-6 instructions. 
; 
;Entry: 
;  EAX = size of local frame 
; 
;Exit: 
;  ESP = new stackframe, if successful 
; 
;Uses: 
;  EAX 
; 
;Exceptions: 
;  _XCPT_GUARD_PAGE_VIOLATION - May be raised on a page probe. NEVER TRAP 
;         THIS!!!! It is used by the OS to grow the 
;         stack on demand. 
;  _XCPT_UNABLE_TO_GROW_STACK - The stack cannot be grown. More precisely, 
;         the attempt by the OS memory manager to 
;         allocate another guard page in response 
;         to a _XCPT_GUARD_PAGE_VIOLATION has 
;         failed. 
; 
;******************************************************************************* 

public _alloca_probe 

_chkstk proc 

_alloca_probe = _chkstk 

     push ecx 

; Calculate new TOS. 

     lea  ecx, [esp] + 8 - 4  ; TOS before entering function + size for ret value 
     sub  ecx, eax    ; new TOS 

; Handle allocation size that results in wraparound. 
; Wraparound will result in StackOverflow exception. 

     sbb  eax, eax    ; 0 if CF==0, ~0 if CF==1 
     not  eax      ; ~0 if TOS did not wrapped around, 0 otherwise 
     and  ecx, eax    ; set to 0 if wraparound 

     mov  eax, esp    ; current TOS 
     and  eax, not (_PAGESIZE_ - 1) ; Round down to current page boundary 

cs10: 
     cmp  ecx, eax    ; Is new TOS 
     jb  short cs20    ; in probed page? 
     mov  eax, ecx    ; yes. 
     pop  ecx 
     xchg esp, eax    ; update esp 
     mov  eax, dword ptr [eax] ; get return address 
     mov  dword ptr [esp], eax ; and put it at new TOS 
     ret 

; Find next lower page and probe 
cs20: 
     sub  eax, _PAGESIZE_   ; decrease by PAGESIZE 
     test dword ptr [eax],eax  ; probe page. 
     jmp  short cs10 

_chkstk endp 

在你的情況,你可能不需要這個複雜的邏輯,這樣的事情就可以了:

xor eax, eax 
    mov ecx, 40  ; alloc 40 pages 
l1: 
    sub esp, 1000h ; move esp one page 
    mov [esp], eax ; touch the guard page 
    loop l1   ; keep looping 
    sub esp, xxxh ; alloc the remaining variables 

有關堆棧的詳細信息,請參閱here和看守頁面。

+0

@ igor-skochinsky-謝謝。有效 – user2004149 2013-03-11 11:41:31