没有合适的资源?快使用搜索试试~ 我知道了~
首页深入理解glibc内存管理:ptmalloc源码详解与问题剖析
深入理解glibc内存管理:ptmalloc源码详解与问题剖析
需积分: 39 30 下载量 175 浏览量
更新于2024-07-17
收藏 1.81MB PDF 举报
本文档深入解析了Glibc内存管理中的核心部分——ptmalloc2源代码,作者是华庭(庄明强),主要针对进阶开发者提供有价值的指导。文章首先介绍了X86平台Linux进程中内存布局,分别阐述了32位和64位模式下的内存组织。接着,概述了操作系统内存分配相关的函数,包括堆操作和mmap映射区域操作。 内存管理部分,文章详细解释了内存管理的一般性概念,包括内存管理的方法、设计目标以及常见的C内存管理程序。对于ptmalloc2,作者着重讲解了其内存管理的核心设计思想,如内存管理的数据结构,如Smallbins、Largebins、Unsortedbin和Fastbins,这些是ptmalloc实现高效内存分配和回收的关键。此外,还分析了malloc_state、Malloc_par等核心结构体,以及配置选项和使用注意事项。 问题分析与解决章节探讨了可能遇到的问题,并提供了相应的解决方案。源代码分析深入到了具体的实现细节,例如边界标记法、分箱式内存管理的原理和实现,以及ptmalloc的初始化过程,包括未初始化时的内存分配与释放、关键函数ptmalloc_init()的运作,以及ptmalloc的锁机制(ptmalloc_lock_all()、ptmalloc_unlock_all()和ptmalloc_unlock_all2())。 最后,文档讨论了多分配区支持,通过Heap_info等数据结构来实现不同大小内存块的灵活管理。整个文档不仅涵盖了理论知识,还结合实际源码,使读者能更好地理解和掌握ptmalloc内存管理的精髓,对于深入理解Glibc内核和优化程序性能具有很高的实用价值。
资源详情
资源推荐
![](https://csdnimg.cn/release/download_crawler_static/11705558/bg10.jpg)
15
的情况下才会调用 mmap()函数向操作系统申请虚拟内存。
主分配区可以访问 heap 区域,如果用户不调用 brk()或是 sbrk()函数,分配程序就可以
保证分配到连续的虚拟地址空间,因为每个进程只有一个主分配区使用 sbrk()分配 heap 区
域的虚拟内存。内核对 brk 的实现可以看着是 mmap 的一个精简版,相对高效一些。如果主
分配区的内存是通过 mmap()向系统分配的,当 free 该内存时,主分配区会直接调用 munmap()
将该内存归还给系统。
当某一线程需要调用 malloc()分配内存空间时,该线程先查看线程私有变量中是否已经
存在一个分配区,如果存在,尝试对该分配区加锁,如果加锁成功,使用该分配区分配内存,
如果失败,该线程搜索循环链表试图获得一个没有加锁的分配区。如果所有的分配区都已经
加锁,那么 malloc()会开辟一个新的分配区,把该分配区加入到全局分配区循环链表并加锁,
然后使用该分配区进行分配内存操作。在释放操作中,线程同样试图获得待释放内存块所在
分配区的锁,如果该分配区正在被别的线程使用,则需要等待直到其他线程释放该分配区的
互斥锁之后才可以进行释放操作。
申请小块内存时会产生很多内存碎片,ptmalloc 在整理时也需要对分配区做加锁操作。
每个加锁操作大概需要 5~10 个 cpu 指令,而且程序线程很多的情况下,锁等待的时间就会
延长,导致 malloc 性能下降。一次加锁操作需要消耗 100ns 左右,正是锁的缘故,导致 ptmalloc
在多线程竞争情况下性能远远落后于 tcmalloc。最新版的 ptmalloc 对锁进行了优化,加入了
PER_THREAD 和 ATOMIC_FASTBINS 优化,但默认编译不会启用该优化,这两个对锁的优化应
该能够提升多线程内存的分配的效率。
3.2.3.2 chunk 的组织
不管内存是在哪里被分配的,用什么方法分配,用户请求分配的空间在 ptmalloc 中都使
用一个 chunk 来表示。用户调用 free()函数释放掉的内存也并不是立即就归还给操作系统,
相反,它们也会被表示为一个 chunk,ptmalloc 使用特定的数据结构来管理这些空闲的 chunk。
1.Chunk 格式
ptmalloc 在给用户分配的空间的前后加上了一些控制信息,用这样的方法来记录分配的
信息,以便完成分配和释放工作。一个使用中的 chunk(使用中,就是指还没有被 free 掉)
在内存中的样子如图所示:
在图中,chunk 指针指向一个 chunk 的开始,一个 chunk 中包含了用户请求的内存区域
和相关的控制信息。图中的 mem 指针才是真正返回给用户的内存指针。chunk 的第二个域
的最低一位为 P,它表示前一个块是否在使用中,P 为 0 则表示前一个 chunk 为空闲,这时
chunk 的第一个域 prev_size 才有效,prev_size 表示前一个 chunk 的 size,程序可以使用这个
值来找到前一个 chunk 的开始地址。当 P 为 1 时,表示前一个 chunk 正在使用中,prev_size
![](https://csdnimg.cn/release/download_crawler_static/11705558/bg11.jpg)
16
无效,程序也就不可以得到前一个 chunk 的大小。不能对前一个 chunk 进行任何操作。ptmalloc
分配的第一个块总是将 P 设为 1,以防止程序引用到不存在的区域。
Chunk 的第二个域的倒数第二个位为 M,他表示当前 chunk 是从哪个内存区域获得的虚
拟内存。M 为 1 表示该 chunk 是从 mmap 映射区域分配的,否则是从 heap 区域分配的。
Chunk 的第二个域倒数第三个位为 A,表示该 chunk 属于主分配区或者非主分配区,如
果属于非主分配区,将该位置为 1,否则置为 0。
空闲 chunk 在内存中的结构如图所示:
当 chunk 空闲时,其 M 状态不存在,只有 AP 状态,原本是用户数据区的地方存储了四
个指针,指针 fd 指向后一个空闲的 chunk,而 bk 指向前一个空闲的 chunk,ptmalloc 通过这
两个指针将大小相近的 chunk 连成一个双向链表。对于 large bin 中的空闲 chunk,还有两个
指针,fd_nextsize 和 bk_nextsize,这两个指针用于加快在 large bin 中查找最近匹配的空闲
chunk。不同的 chunk 链表又是通过 bins 或者 fastbins 来组织的(bins 和 fastbins 在 3.2.3.3
中介绍)。
2.chunk 中的空间复用
为了使得 chunk 所占用的空间最小,ptmalloc 使用了空间复用,一个 chunk 或者正在
被使用,或者已经被 free 掉,所以 chunk 的中的一些域可以在使用状态和空闲状态表示不
同的意义,来达到空间复用的效果。以 32 位系统为例,空闲时,一个 chunk 中至少需要 4
个 size_t(4B)大小的空间,用来存储 prev_size,size,fd 和 bk (见上图), 也就是 16B,
chunk 的大小要对齐到 8B。当一个 chunk 处于使用状态时,它的下一个 chunk 的 prev_size
域肯定是无效的。所以实际上,这个空间也可以被当前 chunk 使用。这听起来有点不可思议,
但确实是合理空间复用的例子。故而实际上,一个使用中的 chunk 的大小的计算公式应该是:
in_use_size = (用户请求大小+ 8 - 4 ) align to 8B,这里加 8 是因为需要存储 prev_size 和 size,
但又因为向下一个 chunk“借”了 4B,所以要减去 4。最后,因为空闲的 chunk 和使用中的
chunk 使用的是同一块空间。所以肯定要取其中最大者作为实际的分配空间。即最终的分配
空间 chunk_size = max(in_use_size, 16)。这就是当用户请求内存分配时,ptmalloc 实际需要
分配的内存大小,在后面的介绍中。如果不是特别指明的地方,指的都是这个经过转换的实
际需要分配的内存大小,而不是用户请求的内存分配大小。
![](https://csdnimg.cn/release/download_crawler_static/11705558/bg12.jpg)
17
3.2.3.3 空闲 chunk 容器
1.Bins
用户 free 掉的内存并不是都会马上归还给系统,ptmalloc 会统一管理 heap 和 mmap 映
射区域中的空闲的 chunk,当用户进行下一次分配请求时,ptmalloc 会首先试图在空闲的
chunk 中挑选一块给用户,这样就避免了频繁的系统调用,降低了内存分配的开销。ptmalloc
将相似大小的 chunk 用双向链表链接起来,这样的一个链表被称为一个 bin。Ptmalloc 一共
维护了 128 个 bin,并使用一个数组来存储这些 bin(如下图所示)。
数组中的第一个为 unsorted bin,数组中从 2 开始编号的前 64 个 bin 称为 small bins,同
一个 small bin 中的 chunk 具有相同的大小。两个相邻的small bin 中的 chunk 大小相差 8bytes。
small bins 中的 chunk 按照最近使用顺序进行排列,最后释放的 chunk 被链接到链表的头部,
而申请 chunk 是从链表尾部开始,这样,每一个 chunk 都有相同的机会被 ptmalloc 选中。
Small bins 后面的 bin 被称作 large bins。large bins 中的每一个 bin 分别包含了一个给定范围
内的 chunk,其中的 chunk 按大小序排列。相同大小的 chunk 同样按照最近使用顺序排列。
ptmalloc 使用“smallest-first,best-fit”原则在空闲 large bins 中查找合适的 chunk。
当空闲的 chunk 被链接到 bin 中的时候,ptmalloc 会把表示该 chunk 是否处于使用中的
标志 P 设为 0(注意,这个标志实际上处在下一个 chunk 中),同时 ptmalloc 还会检查它前
后的 chunk 是否也是空闲的,如果是的话,ptmalloc 会首先把它们合并为一个大的 chunk,
然后将合并后的 chunk 放到 unstored bin 中。要注意的是,并不是所有的 chunk 被释放后就
立即被放到 bin 中。ptmalloc 为了提高分配的速度,会把一些小的的 chunk 先放到一个叫做
fast bins 的容器内。
2.Fast Bins
一般的情况是,程序在运行时会经常需要申请和释放一些较小的内存空间。当分配器合
并了相邻的几个小的 chunk 之后,也许马上就会有另一个小块内存的请求,这样分配器又需
要从大的空闲内存中切分出一块,这样无疑是比较低效的,故而,ptmalloc 中在分配过程中
引入了 fast bins,不大于 max_fast (默认值为 64B)的 chunk 被释放后,首先会被放到 fast bins
中,fast bins 中的 chunk 并不改变它的使用标志 P。这样也就无法将它们合并,当需要给用
户分配的 chunk 小于或等于 max_fast 时,ptmalloc 首先会在 fast bins 中查找相应的空闲块,
然后才会去查找 bins 中的空闲 chunk。在某个特定的时候,ptmalloc 会遍历 fast bins 中的 chunk,
![](https://csdnimg.cn/release/download_crawler_static/11705558/bg13.jpg)
18
将相邻的空闲 chunk 进行合并,并将合并后的 chunk 加入 unsorted bin 中,然后再将 usorted
bin 里的 chunk 加入 bins 中。
3.Unsorted Bin
unsorted bin 的队列使用 bins 数组的第一个,如果被用户释放的 chunk 大于 max_fast,
或者 fast bins 中的空闲 chunk 合并后,这些 chunk 首先会被放到 unsorted bin 队列中,在进
行 malloc 操作的时候,如果在 fast bins 中没有找到合适的 chunk,则 ptmalloc 会先在 unsorted
bin 中查找合适的空闲 chunk,然后才查找 bins。如果 unsorted bin 不能满足分配要求。malloc
便会将 unsorted bin 中的 chunk 加入 bins 中。然后再从 bins 中继续进行查找和分配过程。从
这个过程可以看出来,unsorted bin 可以看做是 bins 的一个缓冲区,增加它只是为了加快分
配的速度。
4.Top chunk
并不是所有的 chunk 都按照上面的方式来组织,实际上,有三种例外情况。Top chunk,
mmaped chunk 和 last remainder,下面会分别介绍这三类特殊的 chunk。top chunk 对于主分
配区和非主分配区是不一样的。
对于非主分配区会预先从 mmap 区域分配一块较大的空闲内存模拟 sub-heap,通过管
理 sub-heap 来响应用户的需求,因为内存是按地址从低向高进行分配的,在空闲内存的最
高处,必然存在着一块空闲 chunk,叫做 top chunk。当 bins 和 fast bins 都不能满足分配需
要的时候,ptmalloc 会设法在 top chunk 中分出一块内存给用户,如果 top chunk 本身不够大,
分配程序会重新分配一个 sub-heap,并将 top chunk 迁移到新的 sub-heap 上,新的 sub-heap
与已有的 sub-heap 用单向链表连接起来,然后在新的 top chunk 上分配所需的内存以满足分
配的需要,实际上,top chunk 在分配时总是在 fast bins 和 bins 之后被考虑,所以,不论 top
chunk 有多大,它都不会被放到 fast bins 或者是 bins 中。Top chunk 的大小是随着分配和回
收不停变换的,如果从 top chunk 分配内存会导致 top chunk 减小,如果回收的 chunk 恰好
与 top chunk 相邻,那么这两个 chunk 就会合并成新的 top chunk,从而使 top chunk 变大。
如果在 free 时回收的内存大于某个阈值,并且 top chunk 的大小也超过了收缩阈值,ptmalloc
会收缩 sub-heap,如果 top-chunk 包含了整个 sub-heap,ptmalloc 会调用 munmap 把整个
sub-heap 的内存返回给操作系统。
由于主分配区是唯一能够映射进程 heap 区域的分配区,它可以通过 sbrk()来增大或是
收缩进程 heap 的大小,ptmalloc 在开始时会预先分配一块较大的空闲内存(也就是所谓
的 heap),主分配区的 top chunk 在第一次调用 malloc 时会分配一块(chunk_size + 128KB)
align 4KB 大小的空间作为初始的 heap,用户从 top chunk 分配内存时,可以直接取出一块内
存给用户。在回收内存时,回收的内存恰好与 top chunk 相邻则合并成新的 top chunk,当 该
次回收的空闲内存大小达到某个阈值,并且 top chunk 的大小也超过了收缩阈值,会执行内
存收缩,减小 top chunk 的大小,但至少要保留一个页大小的空闲内存,从而把内存归还给
操作系统。如果向主分配区的 top chunk 申请内存,而 top chunk 中没有空闲内存,ptmalloc
会调用 sbrk()将的进程 heap 的边界 brk 上移,然后修改 top chunk 的大小。
5.mmaped chunk
当需要分配的 chunk 足够大,而且 fast bins 和 bins 都不能满足要求,甚至 top chunk 本
身也不能满足分配需求时,ptmalloc 会使用 mmap 来直接使用内存映射来将页映射到进程空
间。这样分配的 chunk 在被 free 时将直接解除映射,于是就将内存归还给了操作系统,再
次对这样的内存区的引用将导致 segmentation fault 错误。这样的 chunk 也不会包含在任何
![](https://csdnimg.cn/release/download_crawler_static/11705558/bg14.jpg)
19
bin 中。
6.Last remainder
Last remainder 是另外一种特殊的 chunk,就像 top chunk 和 mmaped chunk 一样,不会
在任何 bins 中找到这种 chunk。当需要分配一个 small chunk,但在 small bins 中找不到合适
的 chunk,如果 last remainder chunk 的大小大于所需的 small chunk 大小,last remainder chunk
被分裂成两个 chunk,其中一个 chunk 返回给用户,另一个 chunk 变成新的 last remainder chuk。
3.2.3.4 sbrk 与 mmap
从进程的内存布局可知,.bss 段之上的这块分配给用户程序的空间被称为 heap (堆)。
start_brk 指向 heap 的开始,而 brk 指向 heap 的顶部。可以使用系统调用 brk()和 sbrk()来增
加标识 heap 顶部的 brk 值,从而线性的增加分配给用户的 heap 空间。在使 malloc 之前,
brk 的值等于 start_brk,也就是说 heap 大小为 0。ptmalloc 在开始时,若请求的空间小于 mmap
分配阈值(mmap threshold,默认值为 128KB)时,主分配区会调用 sbrk()增加一块大小为 (128
KB + chunk_size) align 4KB 的空间作为 heap。非主分配区会调用 mmap 映射一块大小为
HEAP_MAX_SIZE(32 位系统上默认为 1MB,64 位系统上默认为 64MB)的空间作为 sub-heap。
这就是前面所说的 ptmalloc 所维护的分配空间,当用户请求内存分配时,首先会在这个区
域内找一块合适的 chunk 给用户。当用户释放了 heap 中的 chunk 时,ptmalloc 又会使用 fast
bins 和 bins 来组织空闲 chunk。以备用户的下一次分配。若需要分配的 chunk 大小小于 mmap
分配阈值,而 heap 空间又不够,则此时主分配区会通过 sbrk()调用来增加 heap 大小,非主
分配区会调用 mmap 映射一块新的 sub-heap,也就是增加 top chunk 的大小,每次 heap 增
加的值都会对齐到 4KB。
当用户的请求超过 mmap 分配阈值,并且主分配区使用 sbrk()分配失败的时候,或是非
主分配区在 top chunk 中不能分配到需要的内存时,ptmalloc 会尝试使用 mmap()直接映射一
块内存到进程内存空间。使用 mmap()直接映射的 chunk 在释放时直接解除映射,而不再属
于进程的内存空间。任何对该内存的访问都会产生段错误。而在 heap 中或是 sub-heap 中分
配的空间则可能会留在进程内存空间内,还可以再次引用(当然是很危险的)。
当 ptmalloc munmap chunk 时,如果回收的 chunk 空间大小大于 mmap 分配阈值的当前
值,并且小于 DEFAULT_MMAP_THRESHOLD_MAX(32 位系统默认为 512KB,64 位系统默认
为 32MB),ptmalloc 会把 mmap 分配阈值调整为当前回收的 chunk 的大小,并将 mmap 收
缩阈值(mmap trim threshold)设置为 mmap 分配阈值的 2 倍。这就是 ptmalloc 的对 mmap
分配阈值的动态调整机制,该机制是默认开启的,当然也可以用 mallopt()关闭该机制(将在
3.2.6 节介绍如何关闭该机制)。
3.2.4 内存分配概述
1. 分配算法概述,以 32 系统为例,64 位系统类似。
小于等于 64 字节:用 pool 算法分配。
64 到 512 字节之间:在最佳匹配算法分配和 pool 算法分配中取一种合适的。
大于等于 512 字节:用最佳匹配算法分配。
大于等于 mmap 分配阈值(默认值 128KB):根据设置的 mmap 的分配策略进行分配,
如果没有开启 mmap 分配阈值的动态调整机制,大于等于 128KB 就直接调用 mmap
剩余129页未读,继续阅读
![pdf](https://img-home.csdnimg.cn/images/20210720083512.png)
![rar](https://img-home.csdnimg.cn/images/20210720083606.png)
![pdf](https://img-home.csdnimg.cn/images/20210720083512.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://profile-avatar.csdnimg.cn/27c2114f07b843aa9115068ba277278f_lovezncu.jpg!1)
![](https://csdnimg.cn/release/wenkucmsfe/public/img/user-vip.1c89f3c5.png)
笑鼠了
- 粉丝: 33
- 资源: 10
上传资源 快速赚钱
我的内容管理 展开
我的资源 快来上传第一个资源
我的收益
登录查看自己的收益我的积分 登录查看自己的积分
我的C币 登录后查看C币余额
我的收藏
我的下载
下载帮助
![](https://csdnimg.cn/release/wenkucmsfe/public/img/voice.245cc511.png)
最新资源
- 中国微型数字传声器:技术革新与市场前景
- 智能安防:基于Hi3515的嵌入式云台控制系统设计
- 手机电量低时辐射真增千倍?解析手机使用谣言
- 56F803型DSP驱动的高精度大功率超声波电源控制策略研究
- ARM与GPRS结合的远程监测系统设计
- GPS与RFID技术结合的智能巡检系统设计
- CPLD驱动的低功耗爆炸场温度测试系统设计
- 基于FPGA的智能驱动控制系统:可扩展设计与工业网络协议
- 基于ATmega128和CH374的嵌入式USB接口设计
- 基于AT89C52的温度补偿超声波测距仪:高精度设计与应用
- MSP430F448单片机在交流数字电压表中的应用
- 提升变频器应用效率的12项实用技巧
- STM32F103在数字电镀电源并联均流系统中的应用
- PSpice仿真下的升压开关电源设计:拓扑分析与CCM稳定性提升
- 轻巧高效:MSP430主导的低成本无线传感器网络节点设计
- FPGA在EDA/PLD中实现LVDS接口的应用解析
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
![](https://img-home.csdnimg.cn/images/20220527035711.png)
![](https://img-home.csdnimg.cn/images/20220527035711.png)
![](https://img-home.csdnimg.cn/images/20220527035111.png)
安全验证
文档复制为VIP权益,开通VIP直接复制
![](https://csdnimg.cn/release/wenkucmsfe/public/img/green-success.6a4acb44.png)