_chkstk()分析及注意点

_chkstk()这是一个用来分配堆栈用的。源码如下

_chkstk:

push           ecx

cmp            eax,1000h

lea            ecx,[esp+8]

jb             lastpage

probepages:

sub            ecx,1000h

sub            eax,1000h

test           dword ptr [ecx],eax

cmp            eax,1000h

jae            probepages (0040818c)

lastpage:

sub            ecx,eax

mov            eax,esp

test           dword ptr [ecx],eax

mov            esp,ecx

mov            ecx,dword ptr [eax]

mov            eax,dword ptr [eax+4]

push           eax

ret

 

大概流程

1)      “chkstk Routine is a helper routine for the C compiler. For x86

compilers, _chkstk Routine is called when the local variables exceed

4096 bytes; for x64 compilers it is 4K and 8K respectively.”

即, 当一个函数局部变量超过一个页面大小4k的时候, 编译器会自动插入这个函数。插入这个函数的位置在:

push        ebp

mov         ebp,esp

mov         eax,1000D4h

call       _chkstk()

2)      这个函数做什么?当函数为堆栈分配的页面不够时, (堆栈默认大小为1M), 堆栈需要

更多的页面时调用这个函数。

当堆栈使用大于分配的大小(默认1M)时,产生_XCPT_UNABLE_TO_GROW_STACK.

 

3)      当调用这个函数时, 首先外面对eax进行赋值(已经分配的堆栈大小 + 即将分配给函数局部变量的堆栈大小)

 

4)      调用 _chkstk() 首先,保持当期esp到eax中,然后开始判断:如果分配的大小大于一个页面, 到第5)步(大多都先第5)步);否则到第6)步。

 

5)      当需要分配的大小大于一个页面, 则增加一个页面。“sub eax 1000h” 表示堆栈栈顶下移1000h,[由于堆栈是高地址(栈底部)->低地址(栈顶部)分布],所以堆栈扩大了1000h; “sub ecx 1000h”表示分配了1000h(1个页面)之后还需要多少空间;

“test dword ptr [ecx],eax” 表示分配空间,这个时候之前只不过是分配虚存,内存没有 commit  ,这个时候对这个内存地址进行读写操作都会引发一个 page fault 异常(_XCPT_GUARD_PAGE_VIOLATION),  OS捕获这个异常,检查一定的条件,适合的时候就把这个内存页 commit 了,即分配了实际的物理内存。然后再次比较需要多少内存,如果还是超过1页(1000h), 则重复第5)步,否则到第6)步。

 

6)      还需要分配的堆栈空间小于1页的时候,“sub  ecx,eax” 堆栈继续扩大(扩大了剩余大小的空间);然后“mov  esp,ecx”,保存到原来的esp;并且通过“test”为堆栈分配空间。

 

7)      最后,esp的值不再是原来的值,堆栈的大小变成: 堆栈原来大小 + 局部变量需要的堆栈大小 + xx (push 用的一点堆栈的零头).

        

 

   如果你的函数中需要一个很大的局部变量,那就要注意一点了。因为局部变量是在存放在堆栈中的,在分配的时候需要会调用上面的函数,并检测地址是否有效。在这个时刻,如果你是在驱动中,那么很可能会出现蓝屏。在应用层可能会崩溃。因此,如果需要使用比较大的数据时,最好是通过动态分配来得到内存。

 

你可能感兴趣的:(_chkstk()分析及注意点)