为什么`alloca`不检查它是否可以分配内存?

gaa*_*kam 3 c stack-overflow memory-management alloca sbrk

为什么不alloca检查它是否可以分配内存?

来自man 3 alloca:

如果分配导致堆栈溢出,则程序行为未定义....如果无法扩展堆栈帧,则没有错误指示.

为什么alloca不能/不能检查它是否可以分配更多内存?

我理解的方式是alloca在堆栈上分配内存时在堆栈上(s)brk分配内存.来自https://en.wikipedia.org/wiki/Data_segment#Heap:

堆区域由malloc,calloc,realloc和free管理,可以使用brk和sbrk系统调用来调整其大小

来自man 3 alloca:

alloca()函数在调用者的堆栈帧中分配空间的大小字节.

并且堆栈和堆在收敛方向上增长,如此Wikipedia图中所示:

在此输入图像描述

(上图来自Dougimed的Wikimedia Commons,发布于CC BY-SA 3.0)

现在,这两个alloca(s)brk返回一个指向新分配内存的开始,这意味着它们必须知道哪里栈/堆结束当前时刻.的确,来自man 2 sbrk:

以增量0调用sbrk()可用于查找程序中断的当前位置.

因此,他们理解它,检查是否alloca可以分配所需的内存,实质上归结为检查堆栈的当前末端和堆的当前末尾之间是否有足够的空间.如果在堆栈上分配所需的内存会使堆栈到达堆,则分配失败; 否则,它会成功.

那么,为什么不能使用这样的代码来检查是否alloca可以分配内存?

void *safe_alloca(size_t size)
{
    if(alloca(0) - sbrk(0) < size) {
        errno = ENOMEM;
        return (void *)-1;
    } else {
        return alloca(size);
    }
}
Run Code Online (Sandbox Code Playgroud)

这对我来说更加困惑,因为显然(s)brk可以做这样的检查.来自man 2 sbrk:

brk()将数据段的结尾设置为addr指定的值,当该值合理时,系统有足够的内存,并且进程不超过其最大数据大小(请参阅setrlimit(2)).

那么如果(s)brk可以做这样的检查,那么为什么不能alloca呢?

use*_*342 5

alloca是一个非标准的编译器内在的,它的卖点是它编译成非常轻量级的代码,甚至可能是单个指令.它基本上使用局部变量在每个函数的开头执行操作 - 将堆栈指针寄存器移动指定的量并返回新值.不像sbrk,alloca完全是在用户空间,有没有办法知道多少堆栈空间可用的方式.

堆栈向堆堆积的图像是学习内存管理基础知识的有用心智模型,但它在现代系统上并不真实:

  • 正如cmaster在他的回答中所解释的那样,堆栈大小将主要受内核强制执行的限制,而不是堆栈直接冲入堆中,特别是在64位系统上.
  • 在多线程进程中,没有一个堆栈,但是每个线程都有一个堆栈,并且它们显然不能全部向堆增长.
  • 堆本身不是连续的.现代malloc实现使用多个场所来提高并发性能,并将大量分配卸载到匿名mmap,确保free 将它们返回到操作系统.后者也在传统描述的单竞技场"堆"之外.

可以想象一个版本alloca的操作系统从操作系统中查询此信息并返回正确的错误条件,但是它的性能优势将会丢失,甚至可能与之相比malloc(只是偶尔需要去操作系统获取更多内存)对于该过程,通常在用户空间中工作).