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


13

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

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

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

Answers:


1

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

--------------
| Stack      |
--------------
| Free memory|
--------------
| Heap       |
--------------
     .
     .

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

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


“通常堆栈大小不受限制”,不,通常限制为8Mb(ulimit -s)。
Eddy_Em 2013年

是的,您在大多数系统中都是正确的。您正在检查shell的ulimit命令,如果有,则对堆栈大小有硬限制,该限制是无限的(ulimit -Hs)。无论如何,这一点是要强调堆栈和堆向相反的方向增长。
桑托什

那么“自动扩展”与“将元素推入堆栈”有何不同?从您的解释中,我感到它们是相同的。另外,我感觉堆栈和堆的起点超过8MB,因此堆栈可以根据需要增长(或击中堆)。真的吗?如果是,操作系统如何确定堆和堆栈的放置位置?
13

它们是相同的,但是堆栈没有固定的大小,除非您使用rlimit硬限制大小。堆栈放置在虚拟内存区域的末尾,堆紧接在可执行文件的数据段之后。
桑托什

我明白了,谢谢。但是,我认为我没有“堆栈没有固定大小”部分。如果是这种情况,那么8Mb的软限制是多少?
2013年

8

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

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

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

#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;
}

出现提示时,您可以找到该程序的pid(通过ps),然后查看/proc/$THAT_PID/maps原始区域的增长情况。


即使内存区域通过MAP_GROWSDOWN增长了,也可以为原始mem和page_size调用munmap吗?我想是的,因为否则将使用非常复杂的API,但文档未对此明确说明任何内容
i.petruk

2
MAP_GROWSDOWN不应使用,并已从glibc中删除(有关原因,请参阅lwn.net/Articles/294001)。
科林
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.