Heap(一)
ptmalloc、fastbin、tcache概念简述
一、ptmalloc
首先,为减少频繁的系统调用引起的内存管理性能低下,许多libray在实际操作中会直接向kernel申请一大块内存空间,自行用于分配、回收等操作。glibc库使用ptmalloc内存管理器来管理向kernel申请的这一大块内存,以chunk为单元,使用两个基本函数malloc()函数和free()函数来实现chunk的分配和回收等操作。
malloc():将内存大小作为参数并返回指向chunk内存块的指针。
free():释放内存块就是获取一个指向内存的指针并标识为可重用的,根据回收的内存大小来决定分配给何种bin,像内存块小于0x80的就会分配给fastbin。
ptmalloc有一个主分配区main_arena和非主分配区no_main_arena。每一个程序或进程含有一个main_arena和多个no_main_arena。main_arena在程序第一次呼叫malloc()时进行初始化,并向内核申请到了一大块内存空间。main_arena存放在libc中,存放着各种bins链表和top chunk地址信息。根据main_arena中存放的bins链表信息来管理回收的chunk。
以chunk的状态可将chunk分为:Allocated chunk、Free chunk、Top chunk
chunk由chunk header和chunk date组成,不同类型的chunk有着不同的chunk header。
chunk的一个大致结构:
Allocated chunk即是正在使用的chunk,其结构如下:
prev_size/data:表示上一块chunk的size或data,如果chunk是Allocated chunk,那么下一块chunk的这个位置的值会是data。
size:chunk的大小。
A:chunk是否有由其他的arena管理,而不是main arean。值为0时表示由main_arena管理。
M:chunk是否由mmap产生。一般情况下,值为0,表示是从main_arena中产生。
P:表示临近的上一个chunk是否有在使用中。值为1,表示临近的上一个chunk是在使用中的。
由以上结构可知,程序如果用malloc()申请0x20的空间的话,实际的chunk的大小是大于0x20,因为至少要包含chunk header,以及字节对齐的部分。计算得(0x20+0x8+0xf)&~0xf =0x30,实际得chunk大小是0x30。
Free chunk就是chunk header多了两个栏目,fd与bk,其结构如下:
fd(forword pointer):指向下一个free的chunk header。
bk(backword pointer):指向上一个free的chunk header。
这样,就可以使用fd和bk将Free chunk 串联起来。
如果上一块chunk是Free chunk,那么prev_size/data部分是用上一块Free chunk的prev_size来标识,且P值为0,表示上一块chunk是Free chunk。
Top chunk是heap顶端的chunk(众所周知,heap是从低地址走向高地址),即剩余的空闲空间,其结构如下:
二、Fastbin
程序free掉的chunk不会立马被系统回收,而是由ptmalloc统一管理,ptmalloc将size大小差不多的chunk串成一个bin,基于chunk的size大小可将bins分为:Fast bin、Large bin、Small bin、Unsorted bin、Tcache。ptmalloc为提供更高效的内存管理,将chunk_size小于等于global_max_fast的chunk回收至Fastbin。Fastbin的特点就是LIFO(Last In First Out)的一个单向链表,且malloc()在申请fastbin中的内存时,不会去修改这个fastbin中的下一块chunk的P bit值也就是不会置为1。
在free()时,如果下一块chunk是Top chunk,free 掉的这个chunk不会被Top chunk合并。
以glibc2.23为例:
free()时,fastbin中入链程序如下:
对比chunk的size和global_max_fast的值判断是否入fastbin链
通过chunk的size计算应该放在哪个序号的fastbin,fastbin共有七个entry,即[0x20,0x30,0x40…,0x80],例如chunk的size是0x21将会进入0x20的bins,fastbin_index是0,idx
入链
malloc()申请fastbin空间的出链过程自己看看source code叭
https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c
三、tcache
为了加强内存管理的高效性及提高程序的执行效率,从libc2.26开始引出tcache,tcache与fastbin一样是一个单向链表,操作时也是LIFO的模式。程序在free()操作时,free掉的之前会进入的fastbin的chunk,现在会先进到tcache。fastbin的global_max_size是0x80,而tcache提供了更广的range,从0x20直到0x410的64个entry。free掉这类chunk时,不会修改临近的下一个chunk的P bit值。fastbin的fd值是 chunk header,而tcache的fd值是chunk的data,tcache的bk存的是tcache的key值。fastbin header存在libc中,而所谓的tcache header存储在tcache_perthread_struct,指向tcache_perthread_struct的point存在TLS中。
fastbin提供了从0x20到0x80的7个entry,而tcache提供了从0x20至0x410的64个entry,使用count存放entry中chunk的总个数值。count值存放在相当于chunk的低地址位。
规定每个tcache最多接收7个chunk
tcache_perthread_struct结构体内容如下:
其他可以自己看看source code
https://elixir.bootlin.com/glibc/glibc-2.26/source/malloc/malloc.c