malloc()是如何在内部实现的?

时间:2010-08-13 17:35:30

标签: c memory malloc system-calls sbrk

任何人都可以解释malloc()内部如何运作吗?

我有时会完成strace program我看到很多sbrk次系统调用,man sbrk正在讨论它在malloc()中使用但不多了。

3 个答案:

答案 0 :(得分:98)

sbrk系统调用会移动数据段的“边框”。这意味着它移动了一个区域的边界,在该区域中程序可以读/写数据(让它增长或缩小,尽管AFAIK没有malloc确实使用该方法将内存段提供回内核)。除此之外,还有mmap用于将文件映射到内存中,但也用于分配内存(如果需要分配共享内存,mmap就是这样做的。)

所以你有两种从内核获取更多内存的方法:sbrkmmap。关于如何组织从内核获得的内存有各种策略。

一种天真的方式是将其划分为区域,通常称为“桶”,专用于某些结构大小。例如,malloc实现可以为16,64,256和1024字节结构创建存储桶。如果你要求malloc给你一个给定大小的内存,它会将该数字舍入到下一个桶大小,然后从该桶中提供一个元素。如果您需要更大的区域malloc,可以使用mmap直接分配内核。如果特定大小的存储桶为空malloc,则可以使用sbrk为新存储桶腾出更多空间。

有各种malloc设计,并且可能没有一种真正的方法可以实现malloc,因为您需要在速度,开销和避免碎片/空间有效性之间做出妥协。例如,如果存储桶用尽元素,则实现可能会从更大的存储桶中获取元素,将其拆分并将其添加到用尽元素的存储桶中。这将是非常节省空间的,但是对于每种设计都是不可能的。如果您只是通过sbrk / mmap获得另一个可能更快,更简单的存储桶,但效率不高。此外,设计当然必须考虑到“免费”需要以某种方式再次为malloc提供空间。你不只是分发内存而不重用它。

如果您感兴趣,OpenSER / Kamailio SIP代理有两个malloc实现(他们需要自己的实现,因为他们大量使用共享内存而系统malloc不支持共享内存)。请参阅:https://github.com/OpenSIPS/opensips/tree/master/mem

然后你也可以看一下GNU libc malloc implementation,但那个很复杂,IIRC。

答案 1 :(得分:45)

像这样简单的malloc和免费工作:

malloc提供对进程堆的访问。堆是C核库(通常是libc)中的一个构造,它允许对象获得对进程堆上某些空间的独占访问。

堆上的每个分配称为堆单元。这通常由一个标题组成,该标题包含有关单元格大小的信息以及指向下一个堆单元格的指针。这使得堆有效地成为链表。

当启动进程时,堆包含一个单元,其中包含启动时分配的所有堆空间。这个单元格存在于堆的空闲列表中。

当一个人调用malloc时,内存是从malloc返回的大堆单元格中获取的。剩下的就形成了一个新的堆单元,它由内存的其余部分组成。

当释放内存时,堆单元格将添加到堆的空闲列表的末尾。随后的mallocs会在免费列表中查找合适大小的单元格。

可以预料,堆可能会碎片化,堆管理器可能会不时尝试合并相邻的堆单元。

当空闲列表中没有剩余内存用于所需的分配时,malloc调用brk或sbrk,它们是从操作系统请求更多内存页的系统调用。

现在有一些优化堆操作的修改。

  • 用于大内存分配 (通常> 512字节,堆 经理可以直接进入操作系统 分配一个完整的内存页面。
  • 堆 可以指定最小尺寸 分配以防止大量 碎片化。
  • 堆也可以将自己分成一个用于小分配的箱子和一个用于更大分配的箱子,以便更快地进行更大的分配。
  • 还有一些聪明的机制可以优化多线程堆分配。

答案 2 :(得分:7)

同样重要的是要意识到只需用brksbrk移动程序中断指针并不会实际分配内存,它只是设置地址空间。例如,在Linux上,内存将被支持"当访问该地址范围时,通过实际的物理页面,这将导致页面错误,并最终导致内核调用页面分配器以获得后备页面。