这个的目的是什么?
我最近使用了/FAsu
Visual C ++编译器选项来输出一个特别长的成员函数定义的source +程序集。 在汇编输出中,在设置堆栈帧之后,只需调用一个神秘的_chkstk()
函数。
_chkstk()
上的MSDN页面没有解释调用此函数的原因。 我也看到堆栈溢出的问题在堆栈上分配一个页面大小的缓冲区会损坏内存?但我不明白OP和接受的答案在说什么。
_chkstk()
CRT函数的目的是什么? 它有什么作用?
使用时,为您的线程提供额外堆栈的Windows页面。 在堆栈结尾处,有一个防护页映射为不可访问的内存 - 如果程序访问它(因为它试图使用比当前映射更多的堆栈),则存在访问冲突。 操作系统捕获故障,将堆栈的另一页映射到旧守护页相同的地址,创建一个新的守护页,并从导致违规的指令中恢复。
如果一个函数有多个局部变量页面,那么它访问的第一个地址可能会超过堆栈当前的一页。 因此,它会错过守卫页面并触发操作系统没有意识到的访问冲突,因为需要更多的堆栈。 如果所需的总体堆栈特别巨大,甚至可能超出了保护页面,超出了分配给堆栈的虚拟地址空间的末端以及实际用于其他事物的内存。
因此, _chkstk
确保本地变量有足够的空间。 你可以想象它是通过以页面大小的间隔触摸局部变量的内存,以增加顺序来确保它不会错过防护页(所谓的“堆栈探测”)。 我不知道它是否真的这样做,但是,可能需要更直接的路线并指示操作系统映射一定数量的堆栈。 无论哪种方式,如果总需求量大于可用于堆栈的虚拟地址空间,则操作系统可以抱怨它,而不是做一些未定义的事情。
我查看了__chkstk
的代码,并且它每隔一页进行重复的堆栈探测。 所以这样,它不需要对OS进行任何调用。 rax
的参数是要添加的数据的大小。 它确保目标地址(当前rsp
- rax
)可以访问。 如果rax
> rsp
,它会为地址0执行此操作。作为一个有趣的快捷方式,它首先将地址与映射的当前最低页面gs:[10h]
进行比较; 如果目标地址> =这个,那么它什么都不做。
顺便说一下,至少对于64位代码,它拼写为两个下划线: __chkstk__
。
上一篇: What is the purpose of the
下一篇: how to deliberately trigger a StackOverflowException without using recursion?