debug musl( 二 )


宏定义
chunk 的相关宏定义如下:
#define SIZE_ALIGN (4*sizeof(size_t))#define SIZE_MASK (-SIZE_ALIGN)#define OVERHEAD (2*sizeof(size_t))#define MMAP_THRESHOLD (0x1c00*SIZE_ALIGN)#define DONTCARE 16#define RECLAIM 163840#define CHUNK_SIZE(c) ((c)->csize & -2)#define CHUNK_PSIZE(c) ((c)->psize & -2)#define PREV_CHUNK(c) ((struct chunk *)((char *)(c) - CHUNK_PSIZE(c)))#define NEXT_CHUNK(c) ((struct chunk *)((char *)(c) + CHUNK_SIZE(c)))#define MEM_TO_CHUNK(p) (struct chunk *)((char *)(p) - OVERHEAD)#define CHUNK_TO_MEM(c) (void *)((char *)(c) + OVERHEAD)#define BIN_TO_CHUNK(i) (MEM_TO_CHUNK(&mal.bins[i].head))#define C_INUSE((size_t)1)#define IS_MMAPPED(c) !((c)->csize & (C_INUSE))
unbin
将 chunk 从 bins 中取出,并更新。
static void unbin(struct chunk *c, int i) {if (c->prev == c->next)a_and_64(&mal.binmap, ~(1ULL << i));c->prev->next = c->next;c->next->prev = c->prev;c->csize |= C_INUSE;NEXT_CHUNK(c)->psize |= C_INUSE;}
当满足 c->prev != c->next 时可以不将 mal. 清空 。
&
static int alloc_fwd(struct chunk *c) {int i;size_t k;while (!((k = c->csize) & C_INUSE)) {i = bin_index(k);lock_bin(i);if (c->csize == k) {unbin(c, i);unlock_bin(i);return 1;}unlock_bin(i);}return 0;}static int alloc_rev(struct chunk *c) {int i;size_t k;while (!((k = c->psize) & C_INUSE)) {i = bin_index(k);lock_bin(i);if (c->psize == k) {unbin(PREV_CHUNK(c), i);unlock_bin(i);return 1;}unlock_bin(i);}return 0;}
通过当前 chunk 的 csize 检查当前 chunk 是否空闲,如果空闲调用 unbin 函数将当前 chunk 从 bins 链表中取出 。
通过当前 chunk 的 psize 检查当前 chunk 的前一个 chunk 是否空闲,如果空闲调用 unbin 函数将当前 chunk 的前一个 chunk 从 bins 链表中取出 。
函数分析
首先调用检查申请内存大小是否合理并将申请的内存大小转换为 chunk 大小,具体转换规则为加 0x10 然后关于 0x20 向上对齐 。
static int adjust_size(size_t *n) {/* Result of pointer difference must fit in ptrdiff_t. */if (*n - 1 > PTRDIFF_MAX - SIZE_ALIGN - PAGE_SIZE) {if (*n) {errno = ENOMEM;return -1;} else {*n = SIZE_ALIGN;return 0;}}*n = (*n + OVERHEAD + SIZE_ALIGN - 1) & SIZE_MASK;return 0;}if (adjust_size(&n) < 0) return 0;
如果 chunk 大小超过 (即)则直接 mmap 分配 chunk。
if (n > MMAP_THRESHOLD) {size_t len = n + OVERHEAD + PAGE_SIZE - 1 & -PAGE_SIZE;char *base = __mmap(0, len, PROT_READ | PROT_WRITE,MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);if (base == (void *) -1) return 0;c = (void *) (base + SIZE_ALIGN - OVERHEAD);c->csize = len - (SIZE_ALIGN - OVERHEAD);c->psize = SIZE_ALIGN - OVERHEAD;return CHUNK_TO_MEM(c);}
mmap 得到的 chunk 结构如下,注意该 chunk 的 psize 和 csize 的标志位均没有置位且没有下一个 chunk。
如果大小不超过则先通过函数计算 chunk 大小对应的 bin 数组下标 i。然后通过 mal. 获取下标大于等于 i 的非空 bin。接下来是两种情况,如果没有则调用函数扩展堆然后调用将新扩展的堆块和前面空闲的堆块合并然后跳出循环,否则调用函数获取大于等于 i 的最小下标,然后利用或 unbin 将 chunk 从 bin 链表中取出,最终也会跳出循环 。这两种情况最终都会调用 trim 函数,这个函数的作用是从 c 上切下一块 chunk 用于内存分配,剩下的释放掉 。
首先将需要扩展的大小 n 加上 (0x20),之后调用扩展堆并返回扩展后的内存的起始地址 。
/* The argument n already accounts for the caller's chunk* overhead needs, but if the heap can't be extended in-place,* we need room for an extra zero-sized sentinel chunk. */n += SIZE_ALIGN;lock(heap_lock);p = __expand_heap(&n);if (!p) {unlock(heap_lock);return 0;}