目录
堆用来在程序运行时动态的分配内存,对其实就是虚拟空间里从地址向高地址增长的连续的线性区域。
void *malloc(unsigned int size):作用是在内存的动态存储区中分配一个长度为size的连续空间。此函数的返回值是分配区域的起始地址,或者说,此函数是一个指针型函数,返回的指针指向该分配域的开头位置。
void free(void *ptr):释放之前调用 calloc、malloc 或 realloc 所分配的内存空间。
brk():将数据段(.data)的最高地址指针_edata往高地址推。(从堆头开始,参数为地址)
mmap():在进程的虚拟地址空间中(堆和栈中间,称为文件映射区域的地方)找一块空闲的虚拟内存。(分配大于128k)
sbrk():将地址指针往高地址推。(从当前指针位置开始,参数为指针增量)
mummap():删除地址空间。
申请的内存chunk在ptmalloc内部用malloc_chunk结构体表示。
malloc_chunk的一些字段
chunk与mem指针头部的转换
最小的chunk大小
define MINSIZE (unsigned long) (((MIN_CHUNK_SIZE + MALLOC_ALIGN_MASK) &~MALLOC_ALIGN_MASK))(满足SIZE_SZ的最小上界)
检查分配给用户的内存是否对齐
请求字节数判断
将用户请求内存大小转为实际分配内存大小
标记位相关
获取chunk size
获取下一个物理相邻的chunk
获取前一个chunk的信息
当前chunk使用状态相关操作
设置chunk的size字段
获取指定偏移的chunk
指定偏移处chunk使用状态相关操作
根据空闲的 chunk 的大小以及使用状态将 chunk 初步分为4类:fast bins,small bins,large bins,unsorted bin
在用户使用malloc请求分配内存时,ptmalloc2找到的chunk可能并不和申请的内存大小一致,这时候就将分割之后的剩余部分称之为last remainder chunk,unsort bin也会存这一块。top chunk分割剩下的部分不会作为last remainer。
我们知道一个线程申请的1个/多个堆包含很多的信息:二进制位信息,多个malloc_chunk信息等这些堆需要东西来进行管理,那么Arena就是来管理线程中的这些堆的。
程序刚开始执行时,每个线程是没有heap区域的。当其申请内存时,就需要一个结构来记录对应的信息,而heap_info的作用就是这个。而且当该heap的资源被使用完后,就必须得再次申请内存了。此外,一般申请的heap是不连续的,因此需要记录不同heap之间的链接结构。
该数据结构是专门为从Memory Mapping Segment处申请的内存准备的,即为非主线程准备的。
主线程可以通过sbrk()函数扩展program break location获得(直到触及Memory Mapping Segment),只有一个heap,没有heap_info数据结构。
typedef struct _heap_info
{
mstate ar_ptr; /* 堆对应的 arena 的地址 */
struct _heap_info *prev; /* 由于一个线程申请一个堆之后,可能会使用完,之后就必须得再次申请。因此,一个可能会有多个堆。prev即记录了上一个 heap_info 的地址。这里可以看到每个堆的 heap_info 是通过单向链表进行链接的 */
size_t size; /* size 表示当前堆的大小 */
size_t mprotect_size; /* 最后一部分确保对齐 */
/* Make sure the following data is properly aligned, particularly
that sizeof (heap_info) + 2 * SIZE_SZ is a multiple of
MALLOC_ALIGNMENT. */
char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK];
} heap_info;
该结构用于管理堆,记录每个arena当前申请的内存的具体状态,比如说是否有空闲chunk,有什么大小的空闲chunk等等。无论是thread arena还是main arena,它们都只有一个malloc state结构。由于thread的arena可能有多个,malloc state结构会在最新申请的arena中。注意,main arena的malloc_state并不是 heap segment的一部分,而是一个全局变量,存储在libc.so的数据段。
struct malloc_state {
/* 该变量用于控制程序串行访问同一个分配区,当一个线程获取了分配区之后,其它线程要想访问该分配区,就必须等待该线程分配完成候才能够使用。 */
__libc_lock_define(, mutex);
/* flags记录了分配区的一些标志,比如 bit0 记录了分配区是否有 fast bin chunk ,bit1 标识分配区是否能返回连续的虚拟地址空间。 */
int flags;
/* 存放每个 fast chunk 链表头部的指针 */
mfastbinptr fastbinsY[ NFASTBINS ];
/* 指向分配区的 top chunk */
mchunkptr top;
/* 最新的 chunk 分割之后剩下的那部分 */
mchunkptr last_remainder;
/* 用于存储 unstored bin,small bins 和 large bins 的 chunk 链表。 */
mchunkptr bins[ NBINS * 2 - 2 ];
/* ptmalloc 用一个 bit 来标识某一个 bin 中是否包含空闲 chun..*/
unsigned int binmap[ BINMAPSIZE ];
/* Linked list, points to the next arena */
struct malloc_state *next;
/* Linked list for free arenas. Access to this field is serialized
by free_list_lock in arena.c. */
struct malloc_state *next_free;
/* Number of threads attached to this arena. 0 if the arena is on
the free list. Access to this field is serialized by
free_list_lock in arena.c. */
INTERNAL_SIZE_T attached_threads;
/* Memory allocated from the system in this arena. */
INTERNAL_SIZE_T system_mem;
INTERNAL_SIZE_T max_system_mem;
};
函数实现步骤
1、若 get_max_fast() 返回 0,则进行堆的初始化工作,然后进入第 7 步。
2、从 fastbin 中获取一个空闲 chunk。
3、尝试向后合并。
4、若向前相邻 top_chunk,则直接合并到 top_chunk,然后进入第 6 步。
5、否则尝试向前合并后,插入到 unsorted_bin 中。
6、获取下一个空闲 chunk,回到第 2 步,直到所有 fastbin 清空后进入第 7 步。
7、退出函数。
unlink 用来将一个双向链表(只存储空闲的 chunk)中的一个元素取出来,可能在以下地方使用
malloc
Free
malloc_consolidate
realloc
前向扩展,合并物理相邻高地址空闲 chunk(除了top chunk)
在unlink后,拖链的p的fd跟bk的指针都没有变化,我们可以利用这个泄露地址。
libc 地址
泄漏堆地址,双向链表包含多个空闲 chunk
函数实现步骤
1、该函数会首先检查是否有内存分配函数的钩子函数(__malloc_hook)
2、接着会寻找一个 arena 来试图分配内存
3、然后调用 _int_malloc 函数去申请对应的内存
4、如果分配失败的话,ptmalloc 会尝试再去寻找一个可用的 arena,并分配内存
5、如果申请到了 arena,那么在退出之前还得解锁。
6、判断目前的状态是否满足以下条件
+ 要么没有申请到内存
+ 要么是 mmap 的内存
+ 要么申请到的内存必须在其所分配的arena中
7、最后返回内存
1、它根据用户申请的内存块大小以及相应大小 chunk 通常使用的频度(fastbin chunk, small chunk, large chunk),依次实现了不同的分配方法
2、它由小到大依次检查不同的 bin 中是否有相应的空闲块可以满足用户请求的内存
3、当所有的空闲 chunk 都无法满足时,它会考虑 top chunk
4、当 top chunk 也无法满足时,堆分配器才会进行内存块申请
fastbin
small bin
获取 small bin 的索引
获取对应 small bin 中的 chunk 指针
先执行 victim = last(bin),获取 small bin 的最后一个 chunk
如果 victim = bin ,那说明该 bin 为空
如果不相等,那么会有两种情况
第一种情况,small bin 还没有初始化
第二种情况,small bin 中存在空闲的 chunk
large bin
大循环
如果程序执行到了这里,那么说明 与 chunk 大小正好一致的 bin (fast bin, small bin) 中没有 chunk可以直接满足需求 ,但是large chunk 则是在这个大循环中处理
尝试从 unsorted bin 中分配用户所需的内存
尝试从 large bin 中分配用户所需的内存
寻找较大 chunk
尝试从 top chunk 中分配用户所需内存
手机扫一扫
移动阅读更方便
你可能感兴趣的文章