malloc _int_new_arena

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_infomalloc_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_BITtop 指向初始位置。

  • 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 更新为新 Arena a

  • 初始化新 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()

注意事项

  1. 锁顺序list_lock 必须在 free_list_lock 之前获取,free_list_lock 必须在 Arena 锁之前获取。历史上曾因 _int_new_arena 的锁顺序问题导致死锁(BZ #19182)。

  2. 返回时已加锁:调用者拿到 a 后,它处于已锁定状态,必须在使用完后解锁。

  3. Arena 不会被销毁:非主 Arena 一旦创建,就会一直存在于全局链表中,即使其 attached_threads变为 0,也只是被放入 free_list 供复用,不会被 munmap 释放。

  4. 内存对齐: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 在多线程竞争激烈时扩展并发能力的关键一步。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值