2013-03-11 182 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