堆是用malloc函数申请使用的。
假设我们通过void * ptr = malloc(0x10);申请一块内存时:
#include
#include
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:用户申请内存的单位,也是堆管理器管理内存的基本单位 malloc()返回的指针指向一个chunk的数据区域
运行过程中被malloc分配的内存为一个chunk,这块内存在ptmalloc中用malloc_chunk结构体表示,当程序申请的chunk被free时,会被加入相应的空闲管理列表中。
空闲中的chunk(被free 后):
可以发现chunk被free掉,多了红色框框的字段。
chunk神奇之处就在于,chunk虽然由一个统一的结构体声明,但被使用时和空闲时却又有两种不同的状态。
以使用中的chunk为例,可以发现size字段后面三位为A、M、P,用于标记一些属性。因为地址对齐后三位必须为0,所以就将后三位拿来作为标志位使用,这里主要记住P标志位:前一个chunk是否被使用
prev_size字段记录的信息存在两种情况
堆的释放一般都用free函数实现,但是free后chunk去哪了呢?其实在bins中,这块先讲解堆释放管理过程
堆释放后,会被添加到相应的bins中进行管理,这里涉及的结构体是malloc_state。
分箱式管理:对于空闲chunk,ptmalloc采用分箱式内存管理方式,根据空闲chunk大小和处于的状态将其放在四个不同的bin中,这四个空闲的chunk容器包括:fast bins、unsorted bin、small bins和large bins
glibc malloc分配了若干个bins,为了方便查找,glibc提供两个数组:fastbinY和bins
bins其实就是垃圾桶。在这里就是存放没有用的chunk,即被free的chunk
#include
#include
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主要是用于提高小内存的分配效率,默认情况下对于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可以看作是small bins和large bins的cache,只有一个unsorted bin,以双链表管理空闲chunk,空闲chunk不排序
bins用于存储unstored bin,small bins, 和large bins的chunk链表头,所有的chunk在回收时都要放到unsorted bin中,分配时,如果在unsorted bin中没有合适的chunk,就会把unsorted bin中的所有chunk分别加入到所属的bin中,然后再在bin中分配合适的chunk。