任何人都可以解释malloc()
内部如何运作吗?
我有时会完成strace program
我看到很多sbrk
次系统调用,man sbrk
正在讨论它在malloc()
中使用但不多了。
答案 0 :(得分:98)
sbrk
系统调用会移动数据段的“边框”。这意味着它移动了一个区域的边界,在该区域中程序可以读/写数据(让它增长或缩小,尽管AFAIK没有malloc
确实使用该方法将内存段提供回内核)。除此之外,还有mmap
用于将文件映射到内存中,但也用于分配内存(如果需要分配共享内存,mmap
就是这样做的。)
所以你有两种从内核获取更多内存的方法:sbrk
和mmap
。关于如何组织从内核获得的内存有各种策略。
一种天真的方式是将其划分为区域,通常称为“桶”,专用于某些结构大小。例如,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,它们是从操作系统请求更多内存页的系统调用。
现在有一些优化堆操作的修改。
答案 2 :(得分:7)
同样重要的是要意识到只需用brk
和sbrk
移动程序中断指针并不会实际分配内存,它只是设置地址空间。例如,在Linux上,内存将被支持"当访问该地址范围时,通过实际的物理页面,这将导致页面错误,并最终导致内核调用页面分配器以获得后备页面。