malloc()如何在内部实现?

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

我有时做strace program ,我看到很多sbrk系统调用,做man sbrk谈论它在malloc()使用,但没有更多。

sbrk系统调用移动数据段的“边界”。 这意味着它移动了一个程序可以读/写数据的区域的边界(让它增长或缩小,尽pipeAFAIK没有malloc真的把内存段用这种方法返回给内核)。 除此之外,还有mmap用于将文件映射到内存中,但也用于分配内存(如果需要分配共享内存, mmap是如何执行的)。

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

一种天真的做法是把它分成区域,通常称为“桶”,这些区域专用于某些结构尺寸。 例如,一个malloc实现可以为16,64,256和1024个字节的结构创build桶。 如果你要求malloc给你一个给定大小的内存,它把这个数字加到下一个桶的大小,然后给你一个桶中的元素。 如果你需要更大的区域malloc可以使用mmap直接与内核分配。 如果某个大小的存储桶是空的, malloc可以使用sbrk为一个新的存储桶获得更多空间。

有各种各样的mallocdevise,并且没有一个真正的实现malloc方法,因为您需要在速度,开销和避免碎片/空间有效性之间做出妥协。 例如,如果一个存储桶中的元素用完了,那么实现可能会从一个更大的存储桶中获取一个元素,将其分解并将其添加到耗尽元素的存储桶中。 这将是相当节约空间,但不可能与每个devise。 如果你只是通过sbrk / mmap获得另一个桶,可能会更快,更容易,但不是空间效率。 而且,devise当然必须考虑到“免费”需要以某种方式再次向malloc提供空间。 你不仅仅是重复使用内存。

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

那么你也可以看看GNU libc malloc实现 ,但是那个很复杂,IIRC。

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

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

堆上的每个分配称为堆单元。 这通常由一个头部组成,该头部保存关于单元大小的信息以及指向下一个堆单元的指针。 这使堆有效地成为一个链表。

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

当调用malloc时,从malloc返回的大堆单元中获取内存。 剩下的部分被形成一个新的堆单元,由所有其余的内存组成。

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

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

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

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

  • 对于大内存分配(通常> 512字节,堆pipe理器可能会直接进入操作系统并分配一个完整的内存页面。
  • 堆可以指定最小分配大小以防止大量碎片。
  • 这个堆也可以把自己分成一个用于小分配的容器,一个用于较大的分配,以便更快地分配更大的分配。
  • 还有优化multithreading堆分配的巧妙机制。

认识到简单地用brksbrk移动程序中断指针实际上并不分配内存也是很重要的,它只是设置地址空间。 例如,在Linux上,访问该地址范围时,内存将由实际的物理页面“支持”,这将导致页面错误,最终导致内核调用进入页面分配器以获得后台页面。