目录
堆申请
堆简介
堆是用malloc函数申请使用的。
假设我们通过void * ptr = malloc(0x10);申请一块内存时:
-
- 系统会调用一些函数在内存中开辟一大片空间作为堆的分配使用空间。
-
- malloc函数再从这篇堆的分配使用空间中分配0x10大小的空间,将指向该空间的地址返回给ptr(余下的空间称之为topthunk)
- malloc函数再从这篇堆的分配使用空间中分配0x10大小的空间,将指向该空间的地址返回给ptr(余下的空间称之为topthunk)
示例程序
#include <stdio.h>
#include <stdlib.h>
int main(){
void *ptr = malloc(0x10);
void *ptr1= malloc(0x90);
void *ptr2= malloc(0x800);
return 0;
}
我们gdb调试下该程序,执行b main–> vmmap查看内存情况,发现并没有堆空间分配情况:
接下来我们让程序执行第一次调用malloc时,查看内存情况,可以看到存在一块堆空间,并且分配了0x2100大小的堆空间
查看heap空间分配,发现我们明明只malloc(0x10),但却申请了0x21大小的空间。
chunk
定义
chunk:用户申请内存的单位,也是堆管理器管理内存的基本单位 malloc()返回的指针指向一个chunk的数据区域
运行过程中被malloc分配的内存为一个chunk,这块内存在ptmalloc中用malloc_chunk结构体表示,当程序申请的chunk被free时,会被加入相应的空闲管理列表中。
chunk分类
使用中的chunk:已被分配且填写了相应数据
空闲中的chunk(被free 后):
可以发现chunk被free掉,多了红色框框的字段。
chunk神奇之处就在于,chunk虽然由一个统一的结构体声明,但被使用时和空闲时却又有两种不同的状态。
chunk结构体解析
- prev_size:如果前一个chunk是空闲的,该域表示前一个chunk的大小,如果前一个chunk不空闲,该域毫无意义
- size:当前chunk大小,并且记录了当前chunk和前一个chunk的一些属性(通过二进制后三位记录)。
- FD:记录了下一个被free的chunk(used only if free),即下一个被free掉的chunk
- BK:记录了上一个被free的chunk(used only if free),即上一个被free掉的chunk
- fd_nextsize 和 bk_nextsize,largebin使用,记录上/下一个被free chunk的size
以使用中的chunk为例,可以发现size字段后面三位为A、M、P,用于标记一些属性。因为地址对齐后三位必须为0,所以就将后三位拿来作为标志位使用,这里主要记住P标志位:前一个chunk是否被使用
chunk复用技术(prev_size)
prev_size字段记录的信息存在两种情况
- 如果前一个邻接chunk块空闲,那么当前chunk块结构体的prev_size字段记录的是前一个邻接chunk块大小。这就是由当前chunk指针获得前一个空闲chunk地址的依据。
- 如果前一个邻接chunk在使用中,则当前chunk的prev_size的空间被前一个chunk借用,其中的值是前一个chunk的内容,对当前chunk没有任何意义。
堆的释放
堆的释放一般都用free函数实现,但是free后chunk去哪了呢?其实在bins中,这块先讲解堆释放管理过程
堆释放后的管理
堆释放后,会被添加到相应的bins中进行管理,这里涉及的结构体是malloc_state。
分箱式管理:对于空闲chunk,ptmalloc采用分箱式内存管理方式,根据空闲chunk大小和处于的状态将其放在四个不同的bin中,这四个空闲的chunk容器包括:fast bins、unsorted bin、small bins和large bins
bins讲解
glibc malloc分配了若干个bins,为了方便查找,glibc提供两个数组:fastbinY和bins
bins其实就是垃圾桶。在这里就是存放没有用的chunk,即被free的chunk
示例程序
#include <stdio.h>
#include <stdlib.h>
int main(){
void *ptr = malloc(0x10);
void *ptr1= malloc(0x10);
void *ptr2= malloc(0x10);
void *ptr3= malloc(0x10);
free(ptr);
free(ptr1);
free(ptr2);
free(ptr3);
malloc(0x150);
return 0;
}
gdb调试,使其申请4个chunk
这是我们释放2个malloc,查看bins,tcache(thread local caching)是glibc2.26之后引入的一种新机制。他和fastbin很相似,这里就把他当作fastbin就行,无伤大雅!!
我们可以看到0x20处由一个单链表,链着两个地址,这两个地址就是刚刚free的
fast bins
Fast bins主要是用于提高小内存的分配效率,默认情况下对于size_sz为4B的平台,小于64B的chunk分配请求和对于size_sz为8B的平台,小于128B的chunk分配请求,首先会查看fast bins中是否存在所需要大小的chunk存在,如果存在,就直接返回。
fastbinsY拥有10个(上图为7个)元素的数组,用于存放每个fast chunk链表头指针,所以fast bins最多包含10个fast chunk的单链表
unsorted bin
unsorted bin可以看作是small bins和large bins的cache,只有一个unsorted bin,以双链表管理空闲chunk,空闲chunk不排序
bins
bins用于存储unstored bin,small bins, 和large bins的chunk链表头,所有的chunk在回收时都要放到unsorted bin中,分配时,如果在unsorted bin中没有合适的chunk,就会把unsorted bin中的所有chunk分别加入到所属的bin中,然后再在bin中分配合适的chunk。