【问题标题】:when the c++ program stack size used is determined?何时确定使用的 c++ 程序堆栈大小?
【发布时间】:2015-07-09 17:50:53
【问题描述】:

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

但我不知道何时使用的程序堆栈大小(不是最大堆栈大小,只是使用的大小)被固定到操作系统。编译?链接?执行?

像这样:

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

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

最重要的是。 malloc 或 free 时堆大小是否改变?

【问题讨论】:

  • 它是由操作系统在进程执行时分配的。
  • FWIW,栈 size 不固定,它在运行时根据需要扩展,但栈 limit 是固定的。
  • 如果您的程序只使用 40 字节的堆栈(加上运行时系统的更多字节)并且操作系统默认为进程分配 8MB 堆栈(就像 Linux 一样),您仍然有剩下将近 8MB 堆栈。
  • IMO 这个问题非常不清楚。考虑将这些问号中的每一个放在适当的位置,即在格式良好且易读的问题的末尾。例如,“so”不是问题,“if the size if fixed”也不是问题。
  • 嗯...“所以,如果大小是固定的”也不是问题!!!

标签: c++ c stack


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    在加载程序时,堆栈大小未明确提供给操作系统。相反,操作系统使用page faults 机制(如果MMU 支持)。

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

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

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

    由于无限递归和堆栈溢出而出现故障。它实际上需要无限堆栈才能完成。加载程序时,操作系统分配初始堆栈并将其写入%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 值。然后会产生页面错误,操作系统为堆栈提供新的内存页面,允许程序使用它。

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

    【讨论】:

      猜你喜欢
      • 2016-07-15
      • 2010-12-13
      • 2015-02-13
      • 2010-12-22
      • 2011-07-13
      • 2010-09-15
      • 1970-01-01
      相关资源
      最近更新 更多