何时确定使用的c ++程序堆栈大小?

时间:2015-04-30 06:41:54

标签: c++ c stack

我知道链接上的最大堆栈大小通常是固定的(也许是在Windows上)。

但我不知道何时使用的程序堆栈大小(不是刚使用的最大堆栈大小)固定到OS。编译?联系?执行?

像这样:

int main(){ int a[10]; return 0;}

程序只使用10 * sizeof(int)堆栈。那么,堆栈大小是否固定?

首先。如果在malloc或free时更改堆大小?

2 个答案:

答案 0 :(得分:1)

加载程序时,未向OS显式提供堆栈大小。相反,OS使用page faults的机制(如果MMU支持它)。

如果您尝试访问尚未由操作系统授予的内存,MMU会生成由操作系统处理的页面错误。操作系统检查页面错误的地址,并通过创建新的内存页面扩展堆栈,或者如果您的堆栈限制已用尽,则将其处理为堆栈溢出

考虑在x86和Linux上运行以下程序:

void foo(void) {
    volatile int a = 10;
    foo();
}

int main() {
    foo();
}

由于无限递归和堆栈溢出而失败。它实际上需要完成无限堆栈。加载程序时,OS会分配初始堆栈并将其写入%rsp(堆栈指针)。让我们看一下foo()反汇编:

push   %rbp
mov    %rsp,%rbp         <--- Save stackpointer to %rbp
sub    $0x10,%rsp        <--- Advance stack pointer by 16 bytes
movl   $0xa,-0x4(%rbp)   <--- Write memory at %rbp
callq  0x400500 <foo>
leaveq 
retq 

在最多4096/16 = 256个foo()调用之后,您将通过在地址X + 4096处写入内存来中断页边界,其中X是初始%rsp值。然后将生成页面错误,OS为堆栈提供新的内存页面,允许程序使用它。

在大约500k foo()次调用(堆栈的默认Linux ulimit)之后,操作系统将检测到该应用程序使用了太多堆栈页并向其发送SIGSEGV。

答案 1 :(得分:0)

在回答问题时,我提供了以下信息:

BSS / DATA段包含所有全局变量,默认情况下初始化为特定值或零。该段是可执行映像的一部分。在加载时,堆段被添加到此;但是,它不是一个&#34;段&#34;但只是要分配的额外数据量作为加载的BSS / DATA段的扩展。以同样的方式堆栈&#34;段&#34;不是真正的段,而是添加到BSS +堆段。堆栈增长而堆增长。如果这些重叠(使用更多堆并且堆栈仍在增长)和内存不足&#34;错误发生器(堆)或&#34;堆栈溢出&#34; (堆栈) - 这可以通过使用段寄存器(Intel)来检测,以触发硬件生成的异常或使用软件检查。

这是布局细分的传统方式。想想旧的英特尔芯片,其中所有的progeram数据必须是64KB。对于更现代的芯片,通常使用相同的布局,其中在该布局中使用32MB的地址空间但仅使用所需的实际物理存储器。因此堆栈可能非常大。