我已经尝试在我的机器上使用sbrk(1)然后故意写出测试页面大小,这是4096字节.但是当我调用malloc(1)时,我在访问135152字节后获得了SEGV,这超过了一个页面大小.我知道malloc是库函数,它依赖于实现,但考虑到它最终会调用sbrk,为什么它会给出多个页面大小.谁能告诉我它的内部工作?
我的操作系统是ubuntu 14.04,我的架构是x86
更新:现在我想知道是不是因为malloc将地址返回到足以容纳我的数据的空闲列表块.但是该地址可能位于堆的中间,因此我可以继续写入,直到达到堆的上限.
较旧malloc()
的UNIX实现sbrk()
/ brk()
系统调用.但是现在,实现使用mmap()
和sbrk()
.在malloc()
glibc的实施(这可能是你在你的Ubuntu 14.04使用的)同时使用sbrk()
,并mmap()
与选择使用哪一个,当你要求通常取决于分配请求,glibc哪个动态做的大小来分配.
对于小分配,glibc使用sbrk()
它并使用更大的分配mmap()
.宏M_MMAP_THRESHOLD
用于决定这一点.目前,它的默认值设置为128K.这解释了为什么你的代码设法分配135152字节,因为它大约是~128K.即使您只请求了1个字节,您的实现也会分配128K以进行有效的内存分配.因此,在超过此限制之前,不会发生段错误.
您可以通过更改默认参数M_MAP_THRESHOLD
来使用mallopt()
.
M_MMAP_THRESHOLD
对于由M_MMAP_THRESHOLD分配大于或等于指定的(以字节计)的限制时,不能满足从空闲列表,存储器分配函数采用MMAP(2),而不是使用SBRK增加程序中断(2).
使用mmap(2)分配内存具有明显的优势,即分配的内存块总是可以独立地释放回系统.(与此相反,堆可以仅当存储器在顶部端释放修整.).另一方面,也有一些缺点的用途MMAP(2)的:解除分配的空间没有被放置在空闲列表进行再利用上以后的分配; 内存可能会被浪费,因为mmap(2)分配必须是页面对齐的; 并且内核必须执行将通过mmap(2)分配的内存清零的昂贵任务.平衡这些因素会导致M_MMAP_THRESHOLD参数的默认设置为128*1024.
此参数的下限为0. 32位系统上的上限为DEFAULT_MMAP_THRESHOLD_MAX:512*1024或64位系统上的上限为4*1024*1024*sizeof(long).
注意:现在,glibc默认使用动态mmap阈值.阈值的初始值是128×1024,但是,当超过电流阈值且小于或等于DEFAULT_MMAP_THRESHOLD_MAX被释放的块,则阈值被向上调整到释放的块的大小.当动态mmap阈值处于有效状态时,修剪堆的阈值也会动态调整为动态mmap阈值的两倍.如果设置了M_TRIM_THRESHOLD,M_TOP_PAD,M_MMAP_THRESHOLD或M_MMAP_MAX参数中的任何一个,则禁用动态调整mmap阈值.
例如,如果你这样做:
#includemallopt(M_MMAP_THRESHOLD, 0);
在打电话之前malloc()
,你可能会看到不同的限制.其中大部分都是实现细节,而C标准表示,在您的进程不拥有的内存中写入未定义的行为.所以冒着风险 - 否则,恶魔可能会飞出你的鼻子 ;-)