堆栈何时增长?操作系统如何知道何时增长堆栈?

And*_*ndy 6 c memory x86 assembly abi

注意:这个问题是关于 x86_64 架构和 Linux ABI。

当程序启动时,会为堆栈分配一些空间。稍后,在程序执行期间,堆栈区域可以调整大小(当需要更多空间时)达到操作系统指定的某个最大值。

让我们以简单的程序为例:

int main() {
    char bytes[7 * 1024 * 1024];
}
Run Code Online (Sandbox Code Playgroud)

让我们在 gdb 下运行它并设置断点:main 之前和声明数组之后。

gdb> b *main
gdb> b main
gdb> r
gdb> info proc mapping // breakpoint before pushing stack
          Start Addr           End Addr       Size     Offset objfile
      0x7ffffffde000     0x7ffffffff000    0x21000        0x0 [stack]
gdb> c
gdb> info proc mapping // breakpoint after pushing stack
          Start Addr           End Addr       Size     Offset objfile
      0x7fffff8fe000     0x7ffffffff000   0x701000        0x0 [stack]
Run Code Online (Sandbox Code Playgroud)

所以我们可以看到堆栈实际上被调整了大小。

问题是操作系统如何知道何时必须调整堆栈大小?. 一些互联网资源说操作系统处理page fault exception,如果访问的地址在可能的堆栈地址范围内,它会被调整大小。

但是当我一步一步调试程序时,结果是,在执行以下指令后,堆栈的大小就被调整了:

0x40129d <main+4>     sub    rsp, 0x700010
Run Code Online (Sandbox Code Playgroud)

所以(据我所知)还没有page fault,因为我们实际上没有访问地址。我们只更改rsp注册。那么操作系统如何处理它呢?或者也许有一个page fault exception改变后rsp

Aco*_*orn 2

但是当我一步步调试程序时,结果发现,在执行以下指令后,堆栈的大小就被调整了:

0x40129d <main+4>     sub    rsp, 0x700010
Run Code Online (Sandbox Code Playgroud)

这是不正确的。如果您单步执行所显示的程序(即使在没有优化的情况下进行编译),则映射根本不会增加,因为没有对堆栈页进行写入。

但即使您单步执行一个不那么简单的程序,您也会看到映射不会在sub指令上发生变化,而是在每次对堆栈的不断增加的访问上发生变化。

例如,如果您这样做:

bytes[0] = 42;
Run Code Online (Sandbox Code Playgroud)

你会看到它增加了 0x70000,因为你正在写入顶部。但如果你这样做:

bytes[3 * 1024 * 1024] = 42;
Run Code Online (Sandbox Code Playgroud)

您将看到它仅增加了 0x40000。

请注意,如果您遇到的指令恰好压入堆栈,例如call.

  • 正如我在评论中所说: _“注意,gdb 也可能导致堆栈增长,例如,如果您执行 x/a $rsp 或设置了这样做的东西。”_ 您设置了奇特的显示设置,显示每站的堆栈。**那**正在访问内存,因此堆栈会增长。如果您使用普通 gdb,则不会发生这种情况。 (6认同)