glibc 2.31 malloc与free 源码分析(持续更新)

前言

对于 malloc 过程,以 __libc_malloc 函数为入口开始分析,对于 free 过程,以

__libc_malloc (size_t bytes)

void * __libc_malloc (size_t bytes)
{
  mstate ar_ptr;
  void *victim;

  _Static_assert (PTRDIFF_MAX <= SIZE_MAX / 2,
                  "PTRDIFF_MAX is not more than half of SIZE_MAX");
  
  /* 如果存在__malloc_hook,则调用 hook 函数 */
  void *(*hook) (size_t, const void *)
    = atomic_forced_read (__malloc_hook);
  if (__builtin_expect (hook != NULL, 0))
    return (*hook)(bytes, RETURN_ADDRESS (0));

/* 使用 tcache 机制的情况  */
#if USE_TCACHE
  /* int_free also calls request2size, be careful to not pad twice.  */
  size_t tbytes;

  /* 判断请求分配字节的大小,在 64 位的情况下,bytes 不能大于 0x7fffffffffffffff;*/
  /* 在 32 位的情况下,bytes 不能超过 0x7fffffff。函数中也会调用 request2size 来 */ 
  /* 计算 bytes 数据需要分配的内存大小,当 bytes 数据的大小比最小 chunk 要还小时,*/
  /* 按最小 chunk 的大小分配;当 bytes 数据的大小比最小 chunk 大时,则分配满足内存 */
  /* 对齐要求的最小大小。将分配的大小赋值给 tbytes 返回。 */
  if (!checked_request2size (bytes, &tbytes))
    {
      __set_errno (ENOMEM);
      return NULL;
    }

  /* 计算 tbytes 大小所对应的 tcache 下标 */
  size_t tc_idx = csize2tidx (tbytes);
  
  /* 如果 tcache 还没有被创建,则调用 tcache_init() 初始化 tcache */
  MAYBE_INIT_TCACHE ();

  DIAG_PUSH_NEEDS_COMMENT;
  if (tc_idx < mp_.tcache_bins
      && tcachef
      && tcache->counts[tc_idx] > 0)
    {
      return tcache_get(tc_idx);
    }
  DIAG_POP_NEEDS_COMMENT;
#endif

  if (SINGLE_THREAD_P)
    {
      victim = _int_malloc (&main_arena, bytes);
      assert (!victim || chunk_is_mmapped (mem2chunk (victim)) ||
	      &main_arena == arena_for_chunk (mem2chunk (victim)));
      return victim;
    }

  arena_get (ar_ptr, bytes);

  victim = _int_malloc (ar_ptr, bytes);
  /* Retry with another arena only if we were able to find a usable arena
     before.  */
  if (!victim && ar_ptr != NULL)
    {
      LIBC_PROBE (memory_malloc_retry, 1, bytes);
      ar_ptr = arena_get_retry (ar_ptr, bytes);
      victim = _int_malloc (ar_ptr, bytes);
    }

  if (ar_ptr != NULL)
    __libc_lock_unlock (ar_ptr->mutex);

  assert (!victim || chunk_is_mmapped (mem2chunk (victim)) ||
          ar_ptr == arena_for_chunk (mem2chunk (victim)));
  return victim;
}

tcache_init(viod)

static void tcache_init(void)
{
  mstate ar_ptr;
  void *victim = 0;

  /* 计算 tcahce_perthread_struct 结构大小 */
  const size_t bytes = sizeof (tcache_perthread_struct);  

  if (tcache_shutting_down)
    return;

  arena_get (ar_ptr, bytes);
  victim = _int_malloc (ar_ptr, bytes);
  if (!victim && ar_ptr != NULL)
    {
      ar_ptr = arena_get_retry (ar_ptr, bytes);
      victim = _int_malloc (ar_ptr, bytes);
    }


  if (ar_ptr != NULL)
    __libc_lock_unlock (ar_ptr->mutex);

  /* In a low memory situation, we may not be able to allocate memory
     - in which case, we just keep trying later.  However, we
     typically do this very early, so either there is sufficient
     memory, or there isn't enough memory to do non-trivial
     allocations anyway.  */
  if (victim)
    {
      tcache = (tcache_perthread_struct *) victim;
      memset (tcache, 0, sizeof (tcache_perthread_struct));
    }

}

tcache_perthread_struct & tcache_entry

/* 管理 tcache 的结构 */
typedef struct tcache_perthread_struct
{
  uint16_t counts[TCACHE_MAX_BINS];        /* 统计数组中每个下标有多少对应的 chunk ,TCHACHE_MAX_BINS 的值一般是 64 */
  tcache_entry *entries[TCACHE_MAX_BINS];  /* 指向不同 tcache 的指针数组*/
} tcache_perthread_struct;

/* tcache 的基本结构,通过单项链表连接 */
typedef struct tcache_entry
{
  struct tcache_entry *next;              /* 指向下一个 tcache 的指针 */
  /* This field exists to detect double frees.  */
  struct tcache_perthread_struct *key;    /* 新增的防止 tcache double free 的机制 */
} tcache_entry;

tcache_shutting_down

static void tcache_thread_shutdown (void)  
{
  int i;
  tcache_perthread_struct *tcache_tmp = tcache;

  /* tcahce 不存在的情况下直接返回 */
  if (!tcache)
    return;

  /* Disable the tcache and prevent it from being reinitialized.  */
  /* 禁用tcache,防止它被重新初始化 */
  tcache = NULL;
  tcache_shutting_down = true;  /* tcache_shutting_down 的值默认值为 faluse */

  /* Free all of the entries and the tcache itself back to the arena
     heap for coalescing.  */
  /* 释放所有的 tcache ,以便进行合并 */
  /* 外层 for 循环遍历 tcache 指针数组,数组的每个下标对应不同大小的 tcache */
  for (i = 0; i < TCACHE_MAX_BINS; ++i)
    {
      /* 内层 while 循环遍历整个 tcache 点链表,也就是相同大小的 tcache */
      while (tcache_tmp->entries[i])
	{

	  tcache_entry *e = tcache_tmp->entries[i];
	  tcache_tmp->entries[i] = e->next;
	  __libc_free (e);
	}
    }

  __libc_free (tcache_tmp);
}
上一篇:学习大数据的第26天——补充IO流、多线程(1)


下一篇:for循环语句