什么是“自动堆栈扩展”?

lou*_*ear 13 linux process memory limit

getrlimit(2)在手册页中有以下定义:

RLIMIT_AS 进程的虚拟内存(地址空间)的最大大小(以字节为单位)。此限制会影响对 brk(2)、mmap(2) 和 mremap(2) 的调用,超出此限制时会失败并显示错误 ENOMEM。此外,自动堆栈扩展也会失败(如果没有通过 sigaltstack(2) 提供可用的替代堆栈,则会生成一个 SIGSEGV 来终止进程)。由于该值为 long,因此在具有 32 位 long 的机器上,此限制最多为 2 GiB,或者此资源是无限的。

这里的“自动堆栈扩展”是什么意思?Linux/UNIX 环境中的堆栈是否根据需要增长?如果是,确切的机制是什么?

cdl*_*ary 8

确切的机制在这里给出,在 Linux 上:在处理匿名映射上的页面错误时,检查它是否是“增长的分配”,您应该像堆栈一样扩展。如果 VM 区域记录说您应该,那么您调整起始地址以扩展堆栈。

当页面错误发生时,根据地址,它可以通过堆栈扩展得到服务(并消除错误)。虚拟内存的这种“在故障时向下增长”行为可以由任意用户程序请求,并将MAP_GROWSDOWN标志传递给mmap系统调用。

您也可以在用户程序中使用此机制:

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>

int main() {
        long page_size = sysconf(_SC_PAGE_SIZE);
        void *mem = mmap(NULL, page_size, PROT_READ|PROT_WRITE, MAP_GROWSDOWN|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
        if (MAP_FAILED == mem) {
                perror("failed to create growsdown mapping");
                return EXIT_FAILURE;
        }

        volatile char *tos = (char *) mem + page_size;

        int i;
        for (i = 1; i < 10 * page_size; ++i)
                tos[-i] = 42;

        fprintf(stderr, "inspect mappping for originally page-sized %p in /proc... press any key to continue...\n", mem);
        (void) getchar();

        if (munmap(mem, page_size))
                perror("failed munmap");

        return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)

当它提示您找到程序的 pid (via ps) 并查看/proc/$THAT_PID/maps原始区域是如何增长的。

  • MAP_GROWSDOWN 不应使用,并且已从 glibc 中删除(请参阅 https://lwn.net/Articles/294001/ 了解原因)。 (2认同)

小智 1

是的,堆栈动态增长。堆栈位于内存的顶部,向下向堆增长。

--------------
| Stack      |
--------------
| Free memory|
--------------
| Heap       |
--------------
     .
     .
Run Code Online (Sandbox Code Playgroud)

当调用新函数时,堆向上增长(每当执行 malloc 时),而堆栈向下增长。堆位于程序的 BSS 部分上方。这意味着程序的大小及其在堆中分配内存的方式也会影响该进程的最大堆栈大小。通常堆栈大小是无限的(直到堆和堆栈区域相遇和/或覆盖,这将导致堆栈溢出和 SIGSEGV :-)

这仅适用于用户进程,内核堆栈始终是固定的(通常为 8KB)