7

这个问题与C99 中的可变长度数组有关但不同于这个问题

答案指出,在堆栈中分配可变长度数组(或仅是固定大小的大数组)的一个危险是分配可能会默默地失败,而不是调用malloc,它明确告诉调用者分配是否成功。

现代非嵌入式编译平台使用无效内存区域来检测一些堆栈溢出,无需额外费用(检查只是 MMU 已经免费进行的检查)。这并不能 100% 避免上述问题,因为非常大的本地数组可能会导致堆栈指针跳过无效区域。

有谁知道通常为此检测分配多少页?我猜它至少是 4KiB,但可能更多。这是编译器还是操作系统做出的选择,无论哪种情况,有没有办法改变它?

4

2 回答 2

7

我很确定最常见的做法是只使用一页,通常是 4k。然而,一个好的编译器会按顺序尝试访问大于函数入口(或 VLA/alloca分配)页面大小的堆栈帧的每个页面,以确保命中保护页面。GCC 可以选择这样做;请参阅: http: //gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html#Code-Gen-Options-fstack-check选项。

于 2011-04-04T19:51:09.843 回答
6

在 Windows 上,它是一个 4KB 页面(至少在 x86 上):请参阅对基于 Windows NT 的应用程序的堆栈检查的说明

这种自动增长方法使用一个保护页面,一个保留的、未提交的内存页面,它与内存的已提交部分是连续的。当应用程序接触到保护页面时,操作系统提交该页面并且下一个未提交的页面成为新的保护页面。自动堆栈增长仅适用于保护页面,堆栈内存必须以 4K 或一页为增量增长。如果应用程序在接触保护页面之前接触了另一个保留但未提交的堆栈内存页面,则会发生正常的页面错误异常并可能导致不可预知的行为。

...

为防止该错误,每次本地分配超过 4K 时,编译器都会调用 __chkstk() 函数。Windows NT __chkstk() 函数不像 MS-DOS 版本那样显式地检查堆栈溢出。它只是从当前堆栈指针位置到请求的分配每 4K 触摸一次内存地址。这会以正确的顺序触发保护页面,并根据需要向堆栈提交额外的内存。

对于 GCC,GCC 堆栈检查

我不确定 C99 的 VLA 如何/是否会改变 WinNT 的行为。

于 2011-04-04T19:48:36.633 回答