ptmalloc2 中实际创建并初始化一个新非主 Arena 的核心函数。它负责通过 mmap分配堆段、初始化 malloc_state、设置 Top Chunk,并将新 Arena 挂接到全局链表中。
函数签名
static mstate _int_new_arena (size_t size)
参数 size:新 Arena 初始需要容纳的内存大小,用于向系统申请合适的堆段。
返回值:指向新创建的 malloc_state(即 Arena)的指针,失败时返回 NULL。
核心逻辑拆解
1. 分配堆段
h = new_heap (size + (sizeof (*h) + sizeof (*a) + MALLOC_ALIGNMENT), mp_.top_pad);
if (!h)
{
h = new_heap (sizeof (*h) + sizeof (*a) + MALLOC_ALIGNMENT, mp_.top_pad);
if (!h)
return 0;
}
-
调用
new_heap通过mmap分配堆段,大小包含heap_info、malloc_state和请求的size,再加上top_pad填充。 -
如果第一次分配失败(例如
size太大),回退到只申请最小尺寸的堆段,让后续的大请求走mmap路径。 -
如果仍然失败,返回
NULL。
2. 初始化 Arena 结构
a = h->ar_ptr = (mstate) (h + 1); malloc_init_state (a); a->attached_threads = 1; a->system_mem = a->max_system_mem = h->size;
-
h->ar_ptr指向紧随heap_info之后的malloc_state。 -
malloc_init_state初始化所有 bins、设置NONCONTIGUOUS_BIT、top指向初始位置。 -
attached_threads = 1表示当前线程附着在此 Arena 上。 -
记录该 Arena 管理的系统内存总量。
3. 设置 Top Chunk
ptr = (char *) (a + 1); misalign = (unsigned long) chunk2mem (ptr) & MALLOC_ALIGN_MASK; if (misalign > 0) ptr += MALLOC_ALIGNMENT - misalign; top (a) = (mchunkptr) ptr; set_head (top (a), (((char *) h + h->size) - ptr) | PREV_INUSE);
-
从
malloc_state之后开始,对齐到MALLOC_ALIGNMENT边界。 -
将 Top Chunk 指针指向对齐后的位置。
-
设置 Top Chunk 的
size为堆段末尾到 Top Chunk 起始位置的距离,PREV_INUSE标志位设为 1。
4. 绑定线程与更新链表
mstate replaced_arena = thread_arena; thread_arena = a; __libc_lock_init (a->mutex);
-
保存当前线程原先绑定的 Arena(
replaced_arena)。 -
将 TLS 变量
thread_arena更新为新 Arenaa。 -
初始化新 Arena 的互斥锁。
5. 插入全局链表
__libc_lock_lock (list_lock); a->next = main_arena.next; atomic_write_barrier (); main_arena.next = a; __libc_lock_unlock (list_lock);
-
持有
list_lock保护全局 Arena 链表。 -
将新 Arena 插入到
main_arena.next之后(头插法)。 -
atomic_write_barrier确保链表写入对其他线程可见。
6. 解绑原 Arena 并锁定新 Arena
__libc_lock_lock (free_list_lock); detach_arena (replaced_arena); __libc_lock_unlock (free_list_lock); __libc_lock_lock (a->mutex); return a;
-
持有
free_list_lock,调用detach_arena递减原 Arena 的attached_threads计数。 -
最后对新 Arena 加锁,然后返回。调用者拿到的是一个已加锁的 Arena,使用后需要解锁。
调用场景
_int_new_arena 由 arena_get2 调用,而 arena_get2 是 arena_lock / arena_get 宏在 TLS 中没有 Arena 或获取失败时的后备路径。
arena_get2 → 检查 Arena 数量上限
→ 未达上限 → _int_new_arena(size)
→ 已达上限 → reused_arena()
注意事项
-
锁顺序:
list_lock必须在free_list_lock之前获取,free_list_lock必须在 Arena 锁之前获取。历史上曾因_int_new_arena的锁顺序问题导致死锁(BZ #19182)。 -
返回时已加锁:调用者拿到
a后,它处于已锁定状态,必须在使用完后解锁。 -
Arena 不会被销毁:非主 Arena 一旦创建,就会一直存在于全局链表中,即使其
attached_threads变为 0,也只是被放入free_list供复用,不会被munmap释放。 -
内存对齐:Top Chunk 的起始位置经过对齐处理,确保 chunk 满足
MALLOC_ALIGNMENT要求。
总结
| 要点 | 说明 |
|---|---|
| 功能 | 创建并初始化一个新的非主 Arena |
| 堆段来源 | new_heap → mmap |
| 关键操作 | 初始化 malloc_state、设置 Top Chunk、插入全局链表、绑定线程 |
| 锁顺序 | list_lock → free_list_lock → Arena 锁 |
| 返回状态 | Arena 已加锁,调用者需解锁 |
| 生命周期 | 创建后永久存在,不会被销毁,只会在 free_list 中复用 |
一句话理解:
_int_new_arena是 ptmalloc2 为非主线程“开疆拓土”的函数。它通过mmap申请堆段,初始化malloc_state和 Top Chunk,将新 Arena 挂入全局链表,绑定到当前线程,最后返回一个已加锁的 Arena。这是 ptmalloc2 在多线程竞争激烈时扩展并发能力的关键一步。

348

被折叠的 条评论
为什么被折叠?



