我知道链接上的最大堆栈大小通常是固定的(也许是在Windows上)。
但我不知道何时使用的程序堆栈大小(不是刚使用的最大堆栈大小)固定到OS。编译?联系?执行?
像这样:int main(){ int a[10]; return 0;}
程序只使用10 * sizeof(int)堆栈。那么,堆栈大小是否固定?
首先。如果在malloc或free时更改堆大小?
答案 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的地址空间但仅使用所需的实际物理存储器。因此堆栈可能非常大。