每一篇关于内存分配的文章似乎要么解释如何mmap
使用,要么解释如何sbrk
使用,而不考虑如何将它们与堆联系起来。
我发现堆在内存分配中扮演着几乎微不足道的角色——事实上,我不确定它的作用:D,我请求有人不要让我感到困惑。
我是这样理解的:
1) 当内存被malloced时,未初始化数据段的末尾,即BSS,被扩展。这种扩展(x
例如将地址移动到x-n
)是由于调用 而发生的sbrk
。在此模型中,内存被分配到减少 BSS 段头位置的n
字节(假设每个地址对应一个字节) 。sbrk
该模型现已过时。有些人将堆定义为所有此类扩展的聚合空间。其他人则不然——在后一种情况下,堆有什么作用?
2)在现代内存分配方案中,堆确实存在(出于什么原因,我也不确定)。为了分配内存,malloc
内部使用mmap
将数据存储到作为页面集合的内存区域中。这些内存区域独立于堆。
总括:
对于旧系统:如果内存分配存储在增加结束 BSS 的偏移量后获得的地址空间中,那么堆有什么用处吗?
对于较新的系统:假设 mmap 主要用于内存分配,那么堆有什么用途呢?
在这两种情况下,堆真的有什么用处吗?
答案1
mmap
和sbrk
是内核提供的用于为进程分配地址空间的系统调用。这些调用更改虚拟地址到物理页框的映射。对这些映射的地址限制之外的内存进行寻址是严格禁止的,并且会导致分段错误。这是内核向进程提供的低级接口,以brk
地址结尾的内存区域通常称为堆。
内核不知道有关malloc
或 的任何信息free
,这些是 libc 中的库函数。 Libc 维护数据结构并从内存分配的角度记录哪些内存区域是空闲的,例如 .如果可以通过重用先前释放的内存区域来满足对 的调用,则对 的调用malloc
不一定会导致对sbrk
或 的调用mmap
(取决于 Libc 如何实现动态内存分配)来扩展映射。malloc
答案2
“堆”是一个高级的想法,而不是低级的实现。在 C 语言中,堆是malloc()
用于提供分配的内存池。
这是一个有趣、简单的内存分配器实现:
static char *heap[1000000];
int top = 0;
void *malloc(int size) {
void *ret = &heap[top];
top += size;
return ret;
}
void free(void *ptr) {
/* Eh; freeing is too hard */
return;
}
这是一个糟糕的分配器,但是对于正确类型的程序,如果您的系统和编译器对内存对齐之类的事情宽容,那么它就可以工作。它的堆是heap[]
数组,它在编译时使用数组声明来构建它,而不是使用sbrk()
or组装数组mmap()
。
“真正的”分配器以同样的方式工作。当malloc()
被调用时,它会占用一小部分内存池(堆)并将其保留用于该分配。有两个重要的区别:
- 真正的分配器知道如何
free()
分配,以便稍后可以再次使用它。 - 当所有现有块都已满时,实际分配器可以通过向操作系统请求更多内存(使用
sbrk()
或)来增加其堆的大小。mmap()