debug musl( 五 )


函数的作用是将 *phead 指向的 meta 结构从 *phead 指向的 deque 中取出 。
static inline struct meta *dequeue_head(struct meta **phead) {struct meta *m = *phead;if (m) dequeue(phead, m);return m;}
#define PAGESIZE 4096struct malloc_context {uint64_t secret;#ifndef PAGESIZEsize_t pagesize;#endifint init_done;unsigned mmap_counter;struct meta *free_meta_head;struct meta *avail_meta;size_t avail_meta_count, avail_meta_area_count, meta_alloc_shift;struct meta_area *meta_area_head, *meta_area_tail;unsigned char *avail_meta_areas;struct meta *active[48];size_t usage_by_class[48];uint8_t unmap_seq[32], bounces[32];uint8_t seq;uintptr_t brk;};
这个结构体是musl libc的堆管理最上层结构,其中字段的含义分别为:
被实例化为全局变量 ctx。
#define ctx __malloc_context__attribute__((__visibility__("hidden"))) extern struct malloc_context ctx;
struct meta_area {uint64_t check;struct meta_area *next;int nslots;struct meta slots[];};
这个结构用于管理一页内的所有 meta 结构,属于的下级结构,meta 的上级结构 。
meta
struct meta {struct meta *prev, *next;struct group *mem;volatile int avail_mask, freed_mask;uintptr_t last_idx : 5;uintptr_t freeable : 1;uintptr_t sizeclass : 6;uintptr_t maplen : 8 * sizeof(uintptr_t) - 12;};
meta 中保存有 group 结构体指针,后者直接保存有需要分配的内存块 。
以位于 heap 段的为例,内存分布如下图所示,因此 meta 可以通过找所在内存页基址查找到对应的。
与 meta 相关的函数这里先介绍和。
根据 meta 获取其管理的 group 中 chunk 的大小 。
static inline size_t get_stride(const struct meta *g) {if (!g->last_idx && g->maplen) {return g->maplen * 4096UL - UNIT;} else {return UNIT * size_classes[g->sizeclass];}}
释放 meta 实际上就是将 meta 清零后放入指向的 deque 中 。
static inline void free_meta(struct meta *m) {*m = (struct meta){0};queue(&ctx.free_meta_head, m);}
首先判断 ctx 是否初始化,没有初始化则初始化。
if (!ctx.init_done) {#ifndef PAGESIZEctx.pagesize = get_page_size();#endifctx.secret = get_random_secret();ctx.init_done = 1;}
之后初始化。
#define PAGESIZE 4096#ifdef PAGESIZE#define PGSZ PAGESIZE#else#define PGSZ ctx.pagesize#endifsize_t pagesize = PGSZ;if (pagesize < 4096) pagesize = 4096;
如果不为空则从中取出之前释放的 meta 并返回 。
if ((m = dequeue_head(&ctx.free_meta_head))) return m;
如果为 0 则获取空闲的 meta ,之后从空闲的 meta 中取出一个返回 。
if (!ctx.avail_meta_count) {...}ctx.avail_meta_count--;m = ctx.avail_meta++;m->prev = m->next = 0;return m;
下面介绍如何获取空闲的 meta。
首先先解释一下用到的两个标志和的含义 。
这里首先将置 1。
int need_unprotect = 1;
之后讨论没有空闲的且 brk 可以分配连续内存的情况:
if (!ctx.avail_meta_area_count && ctx.brk != -1) {uintptr_t new = ctx.brk + pagesize;int need_guard = 0;if (!ctx.brk) {need_guard = 1;ctx.brk = brk(0);// some ancient kernels returned _ebss// instead of next page as initial brk.ctx.brk += -ctx.brk & (pagesize - 1);new = ctx.brk + 2 * pagesize;}if (brk(new) != new) {ctx.brk = -1;} else {if (need_guard) mmap((void *) ctx.brk, pagesize, PROT_NONE, MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);ctx.brk = new;ctx.avail_meta_areas = (void *) (new - pagesize);ctx.avail_meta_area_count = pagesize >> 12;need_unprotect = 0;}}