内存管理:malloc入口和关键数据结构 前文已经介绍了堆管理的背景知识还有相关算法:虚拟地址空间和堆隐式空闲链表显式空闲链表 从本文开始来分析一下glibc的malloc实现,其中所列的代码都经过精简和修改,现在glibc的最新版本是2.33(https://www.gnu.org/software/libc/),实际的代码太多,不能全部列出,只能尽量简化 一、函数入口 直接在glibc种查找malloc函数的话是找不到的,因为malloc只是一个别名,真正的实现函数名是__libc_malloc: 其中strong_alias是一个宏: __typeof是GNU C(它是标准C的扩展,不严格的说,像ANSI C、ISO C、Standard C、C89、C99都是标准C以不同时期和角度的叫法)中的特性,作用和C++11新出现的decltype有点像,更多细节可以参考GCC10.1的这部分文档:https://gcc.gnu.org/onlinedocs/gcc-10.1.0/gcc/Common-Variable-Attributes.html#Common-Variable-Attributes __attribute__是一个编译属性,它也是GNU C特色之一,用于向编译器描述特殊的标识、错误检查或高级优化。它可以设置函数属性(Function Attribute )、变量属性(Variable Attribute )和类型属性(Type Attribute),上面代码设置的是变量属性(设置变量属性细节可参考GCC10.1的文档:https://gcc.gnu.org/onlinedocs/gcc-10.1.0/gcc/Common-Variable-Attributes.html#Common-Variable-Attributes),语法格式为: 二、malloc_chunk数据结构 malloc中用到的chunk数据结构名称是malloc_chunk,这个数据结构非常重要,是malloc管理堆的基本数据结构,具体定义为: 字段的注释大概说明了这个字段的含义,后面再用图示的方法来详细说下这个数据结构。 三、free chunk 把上面malloc_chunk数据结构直接图形化,结果就是free chunk的示意图,关于allocated chunk,后面会再讲到,这里先展示free chunk:
图1:free chunk 注意上面图1的mchunk_prev_size这个区域类似于前文隐式空闲链表和显式空闲链表中的footer,只是malloc代码把footer部分放到了header之前,后面再具体说为什么。 再来看mchunk_size区域中的A、M、P三个flag的含义,这三个bit flag同样对应前文显式空闲链表中的af flag,只是前文只用了一个bit,这里三个bit全用了,具体的含义如下:A:NON_MAIN_ARENA的缩写,指所用arena是不是main arena的flag,关于arena在虚拟地址空间和堆一文中已经介绍过,这里不再赘述M:IS_MMAPPED的缩写,指所用chunk是不是经由mmap分配所得P:PREV_INUSE的缩写,指当前chunk的前一个chunk是不是allocated chunk,是的话这个bit为1,否则为0 再看fd、bk这两个字段,这个和前文显式空闲链表中的意思相同,分别用来指向forward方向和backward方向的第一个free chunk。fd_nextsize和bk_nextsize也是两个指针,这两个指针只在large chunk中会用到。 四、allocated chunk 先注意一下上面图1注明了是free chunk,因为malloc_chunk中的这些字段,在chunk是free状态的时候按照图1所列字段解释,在chunk状态是allocated的时候,fd、bk、fd_nextsize、bk_nextsize这些字段全部变成用来存放payload(有效数据) 还有mchunk_prev_size这个字段也要注意一下,它有两种解释:在previous chunk是free chunk的时候:这个字段的值是前一个chunk的size在previous chunk是allocated chunk的时候:这个字段里的值可能是前一个chunk的payload(有效数据) 根据前面的这些说明,allocated chunk结构如下图:
图2:allocated chunk 特别需要注意的是,在上面图2,红色部分实际上是next chunk的mchunk_prev_size区域,但是在当前chunk是allocated状态时,next chunk的mchunk_prev_size部分也成了current chunk的payload区域。 这里顺便说一下为什么要把前文显式空闲链表介绍的footer放到了chunk的header之前,之前说过,chunk size和malloc的返回指针mem都要能被8整除,把footer放到header之前并变为mchunk_prev_size字段,这样在mem指针之前就有了8个字节的数据,这样只要当前chunk的开始指针8字节对齐,那么mem指针自然也就8字节对齐,这样处理起来会简单一些,带来的代价就是需要做这些解释。 五、chunk组合情况 本系列接下来所做的分析如果没有特殊说明,都是默认图1的A、M两个bit的flag的值为0,意思就是只main arena和所有的chunk都是经过brk/sbrk分配得到。之所以这样做是因为glibc中malloc的实现非常复杂,考虑了各种情况(包括跨平台、不同版本、多线程支持等),如果文章不做取舍,很难把主线讲清楚,还请大家见谅。 前面讲了free chunk和allocated chunk,这两种chunk组合在一起会产生四种情况:allocated+allocatedallocated+freefree+allocatedfree+free:因为两个free chunk会被merge,通常这种情况不存在,源码里有一种特殊情况,这里暂且不说 下面用图示的方法展示上面1、2、3三种情况的两个chunk的相接部分,这样更容易理解,图中忽略fd_nextsize和bk_nextsize这两个特殊字段:
图3:allocated+allocated 上面图3需要注意的是第二个chunk的mchunk_prev_size区域已经变成了第一个chunk的payload,第二个chunk的P(PREV_INUSE) flag也为1,两个mem指针也必须是8字节对齐。
图4:allocated+free 上面图4值得注意的是下面的free chunk中的fd、bk这些指针字段是有效字段了,也会指向相应的其它free chunk,这个图中没有画出
图5:free+allocated 上面图5需要注意的是下面的chunk的mchunk_prev_size字段是有效字段,存储了上面free chunk的chunk size,同时下面chunk的P(PREV_INUSE) flag也被设为0 六、heap_info和malloc_state 它们是堆管理的核心全局数据结构,之前在虚拟地址空间和堆一文中简单提了一下,这里再来说一遍,因为这两个数据结构太重要了,尤其是malloc_state,后续的文章在用到里面的成员的时候再详细说,大家可以先看下定义了解个大概: 七、小结 文章主要介绍了malloc_chunk这个重要的数据结构,也简单提了下heap_info和malloc_state,整个malloc的代码都会以这几个数据结构为基础,限于篇幅,本文先写到这,后续文章继续分析malloc的代码实现。
2024最新激活全家桶教程,稳定运行到2099年,请移步至置顶文章:https://sigusoft.com/99576.html
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。 文章由激活谷谷主-小谷整理,转载请注明出处:https://sigusoft.com/56295.html