为什么malloc(1)提供多个页面大小?

时间:2015-12-13 09:51:33

标签: c malloc sbrk

我已经尝试在我的机器上使用sbrk(1)然后故意写出超出测试页面大小,即4096字节。但是当我调用malloc(1)时,我在访问135152字节后获得了SEGV,这超过了一个页面大小。我知道malloc是库函数,它依赖于实现,但考虑到它最终会调用sbrk,为什么它会给出多个页面大小。谁能告诉我它的内部工作?

我的操作系统是ubuntu 14.04,我的架构是x86

更新:现在我想知道是不是因为malloc将地址返回到足以容纳我的数据的空闲列表块。但是该地址可能位于堆的中间,这样我就可以继续写入,直到达到堆的上限。

2 个答案:

答案 0 :(得分:8)

UNIX的较早malloc()实现使用sbrk() / brk()系统调用。但是现在,实现使用mmap()sbrk()。 glibc的malloc()实现(可能是你在Ubuntu 14.04上使用的那个)同时使用sbrk()mmap()以及在你请求时使用哪一个来分配通常取决于分配请求的大小,glibc动态地执行。

对于小分配,glibc使用sbrk(),对于较大的分配,它使用mmap()。宏M_MMAP_THRESHOLD用于决定这一点。目前,它的默认值为set to 128K。这解释了为什么你的代码设法分配135152字节,因为它大约是~128K。即使您只请求了1个字节,您的实现也会分配128K以进行有效的内存分配。因此,在超过此限制之前,不会发生段错误。

您可以使用mallopt()通过更改默认参数来使用M_MAP_THRESHOLD

  

M_MMAP_THRESHOLD

     

对于大于或等于限制的分配   由M_MMAP_THRESHOLD指定的(以字节为单位)无法满足   在空闲列表中,内存分配函数使用mmap(2)代替   使用sbrk(2)增加程序中断。

     

使用mmap(2)分配内存具有明显的优势   分配的内存块总是可以独立释放   到系统。 (相比之下,只有内存才可以修剪堆   在最高端被释放。)另一方面,有一些   使用mmap(2)的缺点:未放置释放空间   在免费列表上以供以后分配重用;记忆可能会浪费   因为mmap(2)分配必须是页面对齐的;和内核必须   执行将通过分配的内存归零的昂贵任务   MMAP(2)。平衡这些因素会导致默认设置为   M_MMAP_THRESHOLD参数为128 * 1024。

     

此参数的下限为0.上限为   DEFAULT_MMAP_THRESHOLD_MAX:32位系统上的512 * 1024或   64位系统上的4 * 1024 * 1024 * sizeof(长)。

     

注意:现在,glibc默认使用动态mmap阈值。   阈值的初始值是128 * 1024,但是当块时   大于当前阈值且小于或等于   释放DEFAULT_MMAP_THRESHOLD_MAX,调整阈值   向上移动到释放块的大小。动态mmap时   阈值处于有效状态,修剪堆的阈值也是   动态调整为动态mmap阈值的两倍。动态   如果任何一个,则禁用mmap阈值的调整   M_TRIM_THRESHOLD,M_TOP_PAD,M_MMAP_THRESHOLD或M_MMAP_MAX   参数已设定。

例如,如果你这样做:

#include<malloc.h>

mallopt(M_MMAP_THRESHOLD, 0);

在致电malloc()之前,您可能会看到不同的限制。其中大部分都是实现细节,C标准表示将undefined behaviour写入您的进程不拥有的内存中。所以冒风险 - 否则,demons may fly out of your nose; - )

答案 1 :(得分:3)

出于性能原因,

malloc以大块分配内存。对malloc的后续调用可以为大块提供内存,而不必向操作系统询问大量小块。这减少了所需的系统调用次数。

来自this article

  

当进程需要内存时,通过使用brk()或sbrk()系统调用向前移动堆的上限来创建一些空间。因为系统调用在CPU使用方面是昂贵的,所以更好的策略是调用brk()来获取大块内存,然后根据需要将其拆分以获得更小的块。这正是malloc()所做的。它将许多较小的malloc()请求聚合为较少的大型brk()调用。这样做可以显着提高性能。

请注意,malloc的一些现代实现使用mmap而不是brk / sbrk来分配内存,但除此之外仍然如此。