为调用堆栈分配了多少内存?

How much memory is allocated to call stack?

以前看过很多C++函数的汇编。在 gcc 中,它们都以这些指令开头:

push    rbp
mov     rbp, rsp
sub     rsp, <X>  ; <X> is size of frame

我知道这些指令存储前一个函数的帧指针,然后为当前函数设置一个帧。但是在这里,程序集既不要求映射内存(如 malloc),也不检查 rbp 指向的内存是否已分配给进程。

因此它假定启动代码已为整个调用堆栈深度映射了足够的内存。那么究竟为调用栈分配了多少内存呢?启动代码如何知道调用堆栈的最大深度?

这也意味着,我可以越界远距离访问数组,因为虽然它不在当前帧中,但它映射到进程。所以我写了这段代码:

int main() {
    int arr[3] = {};
    printf("%d", arr[900]);
}

当索引为 900 时,这将以 SIGSEGV 退出。但令人惊讶的是,当索引为 901 时,它不会以 SIGSEGV 退出。同样,对于某些随机索引,它会以 SIGSEGV 退出,而对于某些随机索引则不会。在 compiler explorer.

中使用 gcc-x86-64-11.2 编译时观察到此行为

How does startup code can know the maximum depth of call stack?

没有。

在最常见的实现中,堆栈的大小是恒定的。

如果程序超出常量大小的堆栈,则称为堆栈溢出。这就是为什么你必须避免在自动存储中创建大对象(通常但不一定是数组),以及为什么你必须避免具有线性深度的递归(例如递归链表算法)。

So exactly how much memory is allocated for call stack?

在大多数 desktop/server 系统上,它是可配置的,默认为一到几兆字节。在嵌入式系统上它可以少得多。

This is exiting with SIGSEGV when index is 900. But surprisingly not when index is 901.

在这两种情况下,程序的行为都是未定义的。

Is it possible to know the allocated stack size?

是的。您可以阅读目标系统的文档。如果您打算编写一个可移植程序,那么您必须假定所有目标系统中的最小值。对于desktop/server,我提到的1MB是合理的。

C++ 中没有标准 获取大小的方法。