获取限制(2)手册页中有以下定义:
RLIMIT_AS 进程虚拟内存(地址空间)的最大大小(以字节为单位)。此限制会影响对 brk(2)、mmap(2) 和 mremap(2) 的调用,超过此限制后,这些调用会失败并出现错误 ENOMEM。还自动堆栈扩展将失败(如果没有通过 sigaltstack(2) 提供可用的备用堆栈,则会生成 SIGSEGV 来终止进程)。由于该值是 long,因此在具有 32 位 long 的计算机上,此限制最多为 2 GiB,或者此资源是无限的。
这里的“自动堆栈扩展”是什么意思? Linux/UNIX 环境中的堆栈是否会根据需要增长?如果是,具体机制是什么?
答案1
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;
}
当它提示你找到程序的pid(通过ps
)并查看/proc/$THAT_PID/maps
原始区域的增长情况时。
答案2
是的,堆栈动态增长。堆栈位于内存的顶部,向下向堆增长。
--------------
| Stack |
--------------
| Free memory|
--------------
| Heap |
--------------
.
.
当调用新函数时,堆向上增长(每当执行 malloc 时),而堆栈向下增长。堆位于程序的 BSS 部分上方。这意味着程序的大小及其在堆中分配内存的方式也会影响该进程的最大堆栈大小。通常堆栈大小是无限的(直到堆和堆栈区域相遇和/或覆盖,这将导致堆栈溢出和 SIGSEGV :-)
这仅适用于用户进程,内核堆栈始终是固定的(通常为 8KB)