debug musl( 三 )


如果新扩展的内存的起始地址不等于上一段扩展的内存的结束地址说明内存扩展不连续或者是第一次获取内存,需要在新扩展的 chunk 前面设置一个chunk。
/* If not just expanding existing space, we need to make a* new sentinel chunk below the allocated space. */if (p != end) {/* Valid/safe because of the prologue increment. */n -= SIZE_ALIGN;p = (char *) p + SIZE_ALIGN;w = MEM_TO_CHUNK(p);w->psize = 0 | C_INUSE;}
之后设置新扩展的 chunk 和下一个 chunk 的头部信息 。
/* Record new heap end and fill in footer. */end = (char *) p + n;w = MEM_TO_CHUNK(end);w->psize = n | C_INUSE;w->csize = 0 | C_INUSE;/* Fill in header, which may be new or may be replacing a* zero-size sentinel header at the old end-of-heap. */w = MEM_TO_CHUNK(p);w->csize = n | C_INUSE;unlock(heap_lock);return w;
这里假设连续调用两次 ,则内存分布如下 。由此可知道前面 n += ; 是为了确保如果是不连续或第一次扩展堆时有可以有空间提供chunk 和下一个 chunk 的头部 。
首先检验扩展的大小 n 是否合理,之后将 n 关于页大小向上对齐 。
if (n > SIZE_MAX / 2 - PAGE_SIZE) {errno = ENOMEM;return 0;}n += -n & PAGE_SIZE - 1;
如果 heap 段还没有初始化过则通过 brk(0) 系统调用获取 heap 段基址,并将 brk 关于页面大小向上对齐 。
if (!brk) {brk = __syscall(SYS_brk, 0);brk += -brk & PAGE_SIZE - 1;}
如果满足 brk 调用条件且 brk 调用正常则直接返回得到的内存 。
if (n < SIZE_MAX - brk && !traverses_stack_p(brk, brk + n)&& __syscall(SYS_brk, brk + n) == brk + n) {*pn = n;brk += n;return (void *) (brk - n);}
否则调用 mmap 扩展内存,扩展内存的大小为 max(n,psize 和 self->csize 是否相等 。
struct chunk *next = NEXT_CHUNK(self);.../* Crash on corrupted footer (likely from buffer overflow) */if (next->psize != self->csize) a_crash();
将该 chunk 与前后的空闲 chunk 合并直至满足 self->psize & next->csize &条件,即该 chunk 前后都没有空闲 chunk。
期间如果满足+ size >&& ( + size ^ size) > size (其中为 )则置 1 ,之后会对释放的 chunk 包含的所有完整物理页调用设置标志,这样在内存紧缺的时候会回收这些物理页 。
for (;;) {if (self->psize & next->csize & C_INUSE) {self->csize = final_size | C_INUSE;next->psize = final_size | C_INUSE;i = bin_index(final_size);lock_bin(i);lock(mal.free_lock);if (self->psize & next->csize & C_INUSE)break;unlock(mal.free_lock);unlock_bin(i);}if (alloc_rev(self)) {self = PREV_CHUNK(self);size = CHUNK_SIZE(self);final_size += size;if (new_size + size > RECLAIM && (new_size + size ^ size) > size)reclaim = 1;}if (alloc_fwd(next)) {size = CHUNK_SIZE(next);final_size += size;if (new_size + size > RECLAIM && (new_size + size ^ size) > size)reclaim = 1;next = NEXT_CHUNK(next);}}
之后更新以及 chunk 头部各个字段,然后将 chunk 从对应 bins 的 tail 加入到链表中 。最后对于为 1 的情况做相应的处理 。
if (!(mal.binmap & 1ULL << i))a_or_64(&mal.binmap, 1ULL << i);self->csize = final_size;next->psize = final_size;unlock(mal.free_lock);self->next = BIN_TO_CHUNK(i);self->prev = mal.bins[i].tail;self->next->prev = self;self->prev->next = self;/* Replace middle of large chunks with fresh zero pages */if (reclaim) {uintptr_t a = (uintptr_t) self + SIZE_ALIGN + PAGE_SIZE - 1 & -PAGE_SIZE;uintptr_t b = (uintptr_t) next - SIZE_ALIGN & -PAGE_SIZE;#if 1__madvise((void *) a, b - a, MADV_DONTNEED);#else__mmap((void *)a, b-a, PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0);#endif}unlock_bin(i);