在malloc,为什么要使用brk?为什么不直接使用mmap?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在malloc,为什么要使用brk?为什么不直接使用mmap?相关的知识,希望对你有一定的参考价值。
malloc
的典型实现使用brk
/ sbrk
作为从OS声明内存的主要手段。但是,他们也使用mmap
来获取大量分配的块。使用brk
而不是mmap
有什么好处,还是仅仅是传统?使用mmap
做这一切不会有效吗?
(注意:我在这里可以互换使用sbrk
和brk
,因为它们是同一个Linux系统调用的接口,brk
。)
作为参考,这里有几个描述glibc malloc的文档:
GNU C Library参考手册:GNU分配器 https://www.gnu.org/software/libc/manual/html_node/The-GNU-Allocator.html
glibc wiki:Malloc概述 https://sourceware.org/glibc/wiki/MallocInternals
这些文件描述的是sbrk
用于声称小分配的主要竞技场,mmap
用于声称二级竞技场,而mmap
也用于为大型物体(“远大于一页”)声称空间。
应用程序堆(使用sbrk
声明)和mmap
的使用引入了一些可能不必要的额外复杂性:
分配竞技场 - 主竞技场使用应用程序的堆。其他竞技场使用mmap'd堆。要将块映射到堆,您需要知道哪种情况适用。如果该位为0,则块来自主竞技场和主堆。如果该位为1,则块来自mmap'd内存,并且可以从块的地址计算堆的位置。
[Glibc malloc衍生自ptmalloc,它来源于dlmalloc,始于1987年。]
jemalloc联机帮助页(http://jemalloc.net/jemalloc.3.html)有这样的说法:
传统上,分配器使用sbrk(2)来获取内存,由于多种原因,包括竞争条件,增加的碎片以及对最大可用内存的人为限制,这是次优的。如果操作系统支持sbrk(2),则此分配器按优先顺序使用mmap(2)和sbrk(2);否则只使用mmap(2)。
因此,他们甚至在这里说sbrk
不是最理想的,但他们仍然使用它,即使他们已经去编写代码的麻烦,以便它没有它。
[jemalloc的写作始于2005年。]
更新:考虑到这一点,关于“按照优先顺序”的那一点给了我一条询问线。为什么选择顺序?他们只是使用sbrk
作为后备,以防mmap
不支持(或缺乏必要的功能),或者是否有可能进入一些状态,它可以使用sbrk
而不是mmap
?我会看看他们的代码,看看我能弄清楚它在做什么。
我问,因为我在C中实现垃圾收集系统,到目前为止,除了mmap
之外我没有理由使用任何东西。我想知道是否有一些我缺少的东西。
(在我的情况下,我有另外的理由避免brk
,这是我可能需要在某些时候使用malloc
。)
系统调用brk()
的优点是只有一个数据项来跟踪内存使用情况,这也很好地与堆的总大小直接相关。
这与1975年的Unix V6完全相同。请注意,V6支持65,535字节的用户地址空间。因此,对于管理超过64K,当然不是太字节,没有太多的想法。
使用mmap
似乎是合理的,直到我开始想知道如何改变或添加垃圾收集可以使用mmap,但也没有重写分配算法。
这与realloc()
,fork()
等有效吗?
显而易见的优点是你可以增加最后的分配,这是你无法用mmap(2)
做的事情(mremap(2)
是Linux扩展,不可移植)。
对于使用realloc(3)
的幼稚(而不是那么幼稚)的程序,例如。附加到一个字符串,这转换为1或2个数量级的速度提升;-)
mmap()
在早期版本的Unix中不存在。 brk()
是当时增加流程数据段大小的唯一方法。带有mmap()
的Unix的第一个版本是80年代中期的SunOS
,第一个开源版本是1990年的BSD-Reno。
并且可用于malloc()
,您不希望需要一个真实的文件来备份内存。 1988年,SunOS为此目的实施了/dev/zero
,并在1990年的HP-UX实施了MAP_ANONYMOUS
标志。
现在有mmap()
版本提供了各种分配堆的方法。
每次内存分配调用mmap(2)
对于通用内存分配器来说不是一种可行的方法,因为mmap(2)
的分配粒度(一次可以分配的最小单个单元)是PAGESIZE
(通常是4096字节),并且因为它需要慢和复杂的系统调用。具有低碎片的小分配的分配器快速路径应该不需要系统调用。
所以不管你使用什么策略,你仍然需要支持多个glibc调用内存竞技场,the GNU manual提到:“多个竞技场的存在允许多个线程在不同的场地同时分配内存,从而提高性能。”
jemalloc manpage(http://jemalloc.net/jemalloc.3.html)有这样说:
传统上,分配器使用sbrk(2)来获取内存,由于多种原因,包括竞争条件,增加的碎片以及对最大可用内存的人为限制,这是次优的。如果操作系统支持sbrk(2),则此分配器按优先顺序使用mmap(2)和sbrk(2);否则只使用mmap(2)。
根据我的理解,我不知道这些中的任何一个如何适用于sbrk(2)
的现代用途。竞争条件由线程原语处理。碎片的处理方式与mmap(2)
分配的内存竞技场一样。最大可用内存是无关紧要的,因为mmap(2)
应该用于任何大型分配以减少碎片并在free(3)
上立即将内存释放回操作系统。
使用应用程序堆(声称用sbrk)和mmap引入了一些可能不必要的额外复杂性:
分配竞技场 - 主竞技场使用应用程序的堆。其他竞技场使用mmap'd堆。要将块映射到堆,您需要知道哪种情况适用。如果该位为0,则块来自主竞技场和主堆。如果该位为1,则块来自mmap'd内存,并且可以从块的地址计算堆的位置。
所以现在的问题是,如果我们已经在使用mmap(2)
,为什么不在mmap(2)
的流程开始时分配竞技场而不是使用sbrk(2)
?特别是如果引用的话,有必要跟踪使用哪种分配类型。有几个原因:
- 可能不支持
mmap(2)
。 sbrk(2)
已经初步化为一个过程,而mmap(2)
将引入额外的要求。- 正如glibc wiki所说,“如果请求足够大,则使用mmap()直接从操作系统请求内存[...],并且可能会限制一次可以有多少这样的映射。”
- 用
mmap(2)
分配的内存映射不能轻易扩展。 Linux有mremap(2)
,但它的使用限制了分配器到支持它的内核。使用PROT_NONE
访问预映射许多页面会占用太多虚拟内存。使用MMAP_FIXED
取消映射之前可能没有警告的任何映射。sbrk(2)
没有这些问题,并且明确设计为允许安全地扩展其内存。
以上是关于在malloc,为什么要使用brk?为什么不直接使用mmap?的主要内容,如果未能解决你的问题,请参考以下文章
malloc和free,brk和sbrk和mmap和munmap的使用和关系以及内存分配的原理
Linux 内核 内存管理内存管理架构 ② ( 用户空间内存管理 | malloc | ptmalloc | 内核空间内存管理 | sys_brk | sys_mmap | sys_munmap)