libc2.23 malloc部分源码剖析 – 作者:summerN

malloc源码剖析

一、简介

glibc 内部 malloc() 函数只是__libc_malloc() 函数的别名,而 __libc_malloc() 函数的工作又主要由 _int_malloc() 完成。

因此,分析malloc() 函数,

即是分析 __libc_malloc() 以及 _int_malloc() 这两个函数。

2.24源码

二、源码与解析

1.__libc_malloc()

1.atomic_forced_read()函数

# define atomic_forced_read(x) 
               ({ __typeof (x) __x; __asm ("" : "=r" (__x) : "0" (x)); __x; })
       __typeof是原始函数的返回类型,后面是一段汇编代码,”0”是零,即%0,引用时不可以加 %,
       只能input引用output,这里就是原子读,将__malloc_hook的地址放入任意寄存器(r)再取出

2.关于malloc_hook的初始化问题

ptmalloc 定义了一个全局钩子 __malloc_hook,这个钩子会被赋值为 malloc_hook_ini 函数:(也就是说刚开始malloc_hook的值为malloc_hook_ini 函数的地址,第一次调用的时候会执行malloc_hook_ini )

void *weak_variable (*__malloc_hook)
  (size_t __size, const void *) = malloc_hook_ini;

如果我们需要自定义堆分配函数,那么就可以把 __malloc_hook 重新设置成我们自定义的函数,在 __libc_malloc 的最开始处会判断是否调用 __malloc_hook。也就是说 ptmalloc 给我们提供了一个机会去使用自己定义的堆分配函数来完成对堆空间的申请,申请完成后直接返回,如下:

void *(*hook) (size_t, const void *)
    = atomic_forced_read (__malloc_hook);
  if (__builtin_expect (hook != NULL, 0))
    return (*hook)(bytes, RETURN_ADDRESS (0));

如果我们没有自定义堆分配函数,而是选择默认的 ptmalloc 来帮我们完成申请,那么在用户在第一次调用 malloc 函数时会首先转入 malloc_hook_ini 函数里面,这个函数的定义在 hook.c 文件,如下:

static void *
malloc_hook_ini (size_t sz, const void *caller)
{
  __malloc_hook = NULL;
  ptmalloc_init ();
  return __libc_malloc (sz);
}

可见在 malloc_hook_ini 会把 __malloc_hook 设置为空,然后调用 ptmalloc_init 函数,这个函数的工作是完成对 ptmalloc 的初始化,最后又重复调用 __libc_malloc 函数。

因此可知,在我们第一次调用 malloc 申请堆空间的时候,首先会进入 malloc_hook_ini 函数里面进行对 ptmalloc 的初始化工作,然后再次进入 __libc_malloc 的时候,此时钩子 __malloc_hook 已经被置空了,从而继续执行剩余的代码,即转入 _int_malloc 函数。

换个说法,第一次调用 malloc 函数时函数调用路径如下:

malloc -> __libc_malloc -> __malloc_hook(即malloc_hook_ini) -> ptmalloc_init -> __libc_malloc -> _int_malloc

以后用户再调用 malloc 函数的时候,路径将是这样的:

malloc -> __libc_malloc -> _int_mallocc

3.ptmalloc_init 函数

ptmalloc_init 的定义在 arena.c 文件里面,它里面有这样的一些操作:

static void
ptmalloc_init (void)
{
  if (__malloc_initialized >= 0)
    return;

  __malloc_initialized = 0;

  // 初始化 ptmalloc 操作
  ...
  ...

  __malloc_initialized = 1;
}

进入 ptmalloc_init,首先判断 __malloc_initialized 的值,__malloc_initialized 是一个全局变量,它标记着 ptcmalloc 的初始化状态,如下:

>0 –> 初始化完成
=0 –> 正在初始化
<0 –> 尚未初始化

在 ptmalloc_init 中完成对 ptmalloc 的初始化工作后,置 __malloc_initialized 为 1,表示 ptmalloc 已经被初始化,之后再次进入 ptmalloc_init 时就会直接退出,不会重复初始化。

4.之后对源码的解释

void *
__libc_malloc (size_t bytes)//size_t bytes为我们用户申请的大小
{
  mstate ar_ptr;
  void *victim;

  void *(*hook) (size_t, const void *)
    = atomic_forced_read (__malloc_hook);
  if (__builtin_expect (hook != NULL, 0))
    return (*hook)(bytes, RETURN_ADDRESS (0));

  arena_get (ar_ptr, bytes);
 //获取当前的arena,如果是主线程则获得的是main_arena
  victim = _int_malloc (ar_ptr, bytes);
    //进入_int_malloc函数
  /* Retry with another arena only if we were able to find a usable arena
     before.  */
  if (!victim && ar_ptr != NULL)
      //如果_int_malloc 分配失败,并且我们之前能够找到一个可用arena,可以用另一个arena重试。
    {
      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)
    (void) mutex_unlock (&ar_ptr->mutex);
 //释放mutex引用的互斥锁对象,因为ptmalloc支持多线程
  assert (!victim || chunk_is_mmapped (mem2chunk (victim)) ||
          ar_ptr == arena_for_chunk (mem2chunk (victim)));
  return victim;
}
libc_hidden_def (__libc_malloc)

2._int_malloc()

/*
   ------------------------------ malloc ------------------------------
 */

static void *
_int_malloc (mstate av, size_t bytes)
{
  INTERNAL_SIZE_T nb;               /* normalized request size */
  unsigned int idx;                 /* associated bin index */
  mbinptr bin;                      /* associated bin */

  mchunkptr victim;                 /* inspected/selected chunk */
  INTERNAL_SIZE_T size;             /* its size */
  int victim_index;                 /* its bin index */

  mchunkptr remainder;              /* remainder from a split */
  unsigned long remainder_size;     /* its size */

  unsigned int block;               /* bit map traverser */
  unsigned int bit;                 /* bit map traverser */
  unsigned int map;                 /* current word of binmap */

  mchunkptr fwd;                    /* misc temp for linking */
  mchunkptr bck;                    /* misc temp for linking */

  const char *errstr = NULL;

  /*
     Convert request size to internal form by adding SIZE_SZ bytes
     overhead plus possibly more to obtain necessary alignment and/or
     to obtain a size of at least MINSIZE, the smallest allocatable
     size. Also, checked_request2size traps (returning 0) request sizes
     that are so large that they wrap around zero when padded and
     aligned.
   */

  checked_request2size (bytes, nb);
//这里将我们用户申请的大小转化为对应chunk的大小,nb就是我们转化后的大小
  /* There are no usable arenas.  Fall back to sysmalloc to get a chunk from
     mmap.  */
  if (__glibc_unlikely (av == NULL))
    {
      void *p = sysmalloc (nb, av);
      if (p != NULL)
    alloc_perturb (p, bytes);
      return p;
    }
////传入的参数av是在上面__libc_malloc中调用arena_get获得的分配去指针,如果为null,就表示没有分配区可用,这时候就直接调用sysmalloc通过mmap获取chunk
  /*
     If the size qualifies as a fastbin, first check corresponding bin.
     This code is safe to execute even if av is not yet initialized, so we
     can try it without checking, which saves some time on this fast path.
   */
//从 fastbin 中分配 chunk 
  if ((unsigned long) (nb) <= (unsigned long) (get_max_fast ()))
//get_max_fast返回fastbin可以存储内存的最大值,它在ptmalloc的初始化函数malloc_init_state中定义。
//如果需要分配的内存大小nb落在fastbin的范围内,我么尝试从 fast bins 中 分配 chunk
    {
      idx = fastbin_index (nb);
/*
		加入我们的chunk大小是0x20(最小也是0x20),那么我们的索引就是(64位)
		1.0x20 >> 4 = 2
		2.2 -2 = 0
		3.idx = 0
        #define fastbin_index(sz) 
            ((((unsigned int) (sz)) >> (SIZE_SZ == 8 ? 4 : 3)) - 2)
        减2是根据fastbin存储的内存最小值计算的,32位为4,64位为8,假设SIZE_SZ=8,因此改写后
        idx = (nb>>4)-2
    */
      mfastbinptr *fb = &fastbin (av, idx);
      //#define fastbin(ar_ptr, idx) ((ar_ptr)->fastbinsY[idx])
      //根据idx获取对应链表的头指针
      mchunkptr pp = *fb;//获取对应大小的fatbin的链表中的第一个空闲的chunk
      do
        {
          victim = pp;
          if (victim == NULL)
            break;
        }
      while ((pp = catomic_compare_and_exchange_val_acq (fb, victim->fd, victim))
             != victim);
 //catomic_compare_and_exchange_val_rel 功能是 如果*fb等于victim,则将*fb存储为victim->fd,返回victim;
//pp = victim == victim 导致循环退出
//作用为从刚刚得到的空闲chunk链表指针中取出第一个空闲的chunk(victim),并将链表头设置为该空闲chunk的下一个chunk(victim->fd)
//此时victim是我们取出的chunk的地址
      if (victim != 0)
        {
          if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0))
 //这里是检查我们取出的chunk的size所对应的索引是否等于我们之前已经得到的索引
 //其实这里就是在检查我们取出的chunk的size是否与我们申请的fastbin的大小相同(这也是malloc的唯一检查)
            {
              errstr = "malloc(): memory corruption (fast)";
            errout:
              malloc_printerr (check_action, errstr, chunk2mem (victim), av);
              return NULL;
            }
          check_remalloced_chunk (av, victim, nb);
////# define check_remalloced_chunk(A, P, N)
          //check_remalloced_chunk 这个函数其他博客说什么也没有实现
          void *p = chunk2mem (victim);//把chunk的指针转换成mem的指针
          alloc_perturb (p, bytes);
         /*
           static void  alloc_perturb (char *p, size_t n)
           {
            if (__glibc_unlikely (perturb_byte))
               memset (p, perturb_byte ^ 0xff, n);
           }
        */
        /*这里就是向p(chunk的地址)+0x10填充字符(大小是我们申请的大小),但是貌似
        啥时候填充不知到,,就直接忽略把,其实啥也没有发生*/
          return p;//反回我们的chunk
        }
    }

  /*
     If a small request, check regular bin.  Since these "smallbins"
     hold one size each, no searching within bins is necessary.
     (For a large request, we need to wait until unsorted chunks are
     processed to find best fit. But for small ones, fits are exact
     anyway, so we can check now, which is faster.)
   */
//如果在 fastbins 里面没有找到满足要求的空闲 chunk 或者 nb 不属于 fastbins,并且 nb 属于 smallbins
  if (in_smallbin_range (nb))
    {
      idx = smallbin_index (nb);
      /*
       #define smallbin_index(sz) (
        (SMALLBIN_WIDTH == 16 ? (((unsigned) (sz)) >> 4) : (((unsigned) (sz)) >> 3))
        )
       如果是64位,size>>4就是smallbin_index,32位则是size>>3
    */
     //idx为获取的索引指针
      bin = bin_at (av, idx);
//获得索引idx后,就通过idx获取对应的small bin链表表头指针
      if ((victim = last (bin)) != bin)
//获取对应链表的最后一个chunk为victim然后与bin进行比较,也就是和链表头进行比较,如果比较的结果为相等,那么该链表为空,跳过下面部分,进入检查整理unsorted bin的行列,若不相等,则进入下面的代码
        {
          if (victim == 0) /* initialization check */
              //victim为0表示smallbin还未初始化
            malloc_consolidate (av);//调用malloc_consolidate完成初始化操作
          else
            {
              bck = victim->bk;
              //获取此chunk的下一个chunk的地址
    	     if (__glibc_unlikely (bck->fd != victim))//small bin 采取先进先出的原则
        //这里检查victim下一个chunk的fd是否与victim相等
        //这里也是small bin的唯一检查
                {
                  errstr = "malloc(): smallbin double linked list corrupted";
                  goto errout;
                }
              set_inuse_bit_at_offset (victim, nb);
  /*     #define set_inuse(p)							      \
  ((mchunkptr) (((char *) (p)) + ((p)->size & ~SIZE_BITS)))->size |= PREV_INUSE
  */
              //此函数为将下一个物理地址相邻的chunk的 inuse bit 为 1
              //size & ~SIZE_BITS,这个为size大小去掉标志位的结果,得到(p + (p)->size & ~SIZE_BITS))也就是说获取相邻物理地址下一个chunk的地址
              bin->bk = bck;
              bck->fd = bin;
    //双向链表 bin->bk = bck;链表头的bk为victim下一个chunk的地址
    // bck->fd = bin victim下一个chunk的fd设置为链表头地址
              if (av != &main_arena)
                victim->size |= NON_MAIN_ARENA;//如果不是主线程则设置NON_MAIN_ARENA位
              check_malloced_chunk (av, victim, nb);//默认没有做任何操作
              void *p = chunk2mem (victim);//这里就和之前相同了将chunk指针转化为mem指针
              alloc_perturb (p, bytes);//默认不做任何操作
              return p;
            }
        }
    }

  /*
     If this is a large request, consolidate fastbins before continuing.
     While it might look excessive to kill all fastbins before
     even seeing if there is space available, this avoids
     fragmentation problems normally associated with fastbins.
     Also, in practice, programs tend to have runs of either small or
     large requests, but less often mixtures, so consolidation is not
     invoked all that often in most programs. And the programs that
     it is called frequently in otherwise tend to fragment.
   */

  else//这里就是我们申请的大小属于large bin
    {
      idx = largebin_index (nb);//获取对应大小的large bin的索引
      if (have_fastchunks (av))//这里是检查fastbin链表是否为空,若为空就没必要执行malloc_consolidate (av);来合并fastbin了,如果不为空则执行malloc_consolidate (av)
      //标记 fastbins 是否为空的是分配区管理的一个数据成员 flags,在 free 函数中当被释放空间插入到 fastbins 中时这个数据成员被设置,在 malloc_consolidate 函数中整理 fastbins 时这个数据成员被清除。
           /*
        #define FASTCHUNKS_BIT        (1U)
        #define have_fastchunks(M)     (((M)->flags & FASTCHUNKS_BIT) == 0)
  
    */
        malloc_consolidate (av);
    }
//malloc_consolidate (av);函数:
/*fastbins中的chunk都整理到unsortedbin中,整理的过程中如果有物理相邻且空闲的fastchunk就合并,如果fastchunk与topchunk相邻,那么fastchunk就与topchunk合并(这个过程发生在_int_malloc函数调用的malloc_consolidate函数中)
     (注意这里是把fastbin中的所有块清空)*/
  /*
     Process recently freed or remaindered chunks, taking one only if
     it is exact fit, or, if this a small request, the chunk is remainder from
     the most recent non-exact fit.  Place other traversed chunks in
     bins.  Note that this step is the only place in any routine where
     chunks are placed in bins.

     The outer loop here is needed because we might not realize until
     near the end of malloc that we should have consolidated, so must
     do so and retry. This happens at most once, and only when we would
     otherwise need to expand memory to service a "small" request.

   */

  for (;; )
    {
      int iters = 0;
      while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
          //如果没有链表为空,那么unsorted_chunks (av)->bk) = unsorted_chunks (av) = main_arnea中的top
          //如果链表不为空,则循环遍历所有的chunk
        {
          bck = victim->bk;
          if (__builtin_expect (victim->size <= 2 * SIZE_SZ, 0)
              || __builtin_expect (victim->size > av->system_mem, 0))
            malloc_printerr (check_action, "malloc(): memory corruption",
                             chunk2mem (victim), av);
          size = chunksize (victim);
          /*
         检查当前遍历的 chunk 是否合法,chunk 的大小不能小于等于 2 * SIZE_SZ,
         也不能超过 该分配区总的内存分配量。然后获取 chunk 的大小并赋值给 size。
         这里的检查似乎有点小问 题,直接使用了 victim->size,但 victim->size 
         中包含了相关的标志位信息,使用 chunksize(victim) 才比较合理,但在 
         unsorted bin 中的空闲 chunk 的所有标志位都清零了,所以这里直接 
         victim->size 没有问题。
         */

          /*
             If a small request, try to use last remainder if it is the
             only chunk in unsorted bin.  This helps promote locality for
             runs of consecutive small requests. This is the only
             exception to best-fit, and applies only when there is
             no exact fit for a small chunk.
           */
    
          if (in_smallbin_range (nb) &&
              bck == unsorted_chunks (av) &&
              victim == av->last_remainder &&
              (unsigned long) (size) > (unsigned long) (nb + MINSIZE))
               /*
         如果要申请的大小在smallbin范围 且 unsorted chunks 只有一个chunk,且
         victim是last_remainder 且 victim的size大于请求的chunk的大小nb加上
         (MINSIZE)最小chunk的size,那么就切割remainder,然后返回victim。
         即满足四个条件:
         
       申请空间 nb 在 smallbins 范围内
       unsortedbin 仅有唯一一个空闲 chunk
       唯一的一个空闲 chunk 是 last_remainder
       唯一一个空闲 chunk 的大小可以进行切割

         
		如果在bins链(不包括fastbin)中存在freechunk时,当我们去malloc的时候,malloc的请求大小比freechunk的大小小,那么arena就会切割这个freechunk给malloc使用,那么切割之后剩余的chunk就被称为“last remainder”
当产生last remainder之后,表示arena的malloc_state结构体中的last_remainder成员指针就会被初始化,并且指向这个last remainder
     */
            {
              /* split and reattach remainder */
 //分割reminder,并将新的reminder插入到unsorted bin中
              remainder_size = size - nb;//计算剩下reminder的大小
              remainder = chunk_at_offset (victim, nb);//获取剩下的reminder的地址
              unsorted_chunks (av)->bk = unsorted_chunks (av)->fd = remainder;//将新的reminder插入到unsorted bin中
              av->last_remainder = remainder;//last_reminder重新指向新的reminder
              remainder->bk = remainder->fd = unsorted_chunks (av);//新reminder的fd与bk指向unsorted bin 的链表头,注意,unsorted bin链表头并不是malloc_chunk结构体,而是main_arena变量中bins列表的前两项分别做fd和bk指针,指向的位置也不是pre_size,而是main_arena中的top,top指向top chunk。
              if (!in_smallbin_range (remainder_size))
               //如果新的remind的size不在small bin中,而是在large bin中,那么将则把                                  fd_nextsize,fd_nextsize清零
                {
                  remainder->fd_nextsize = NULL;
                  remainder->bk_nextsize = NULL;
                }
    
              set_head (victim, nb | PREV_INUSE |
                        (av != &main_arena ? NON_MAIN_ARENA : 0));//设置victim的size
              set_head (remainder, remainder_size | PREV_INUSE);//设置remainder的size
              set_foot (remainder, remainder_size);//设置remainder的物理相邻的下一个chunk的prev_size
    
              check_malloced_chunk (av, victim, nb);///默认没有做任何操作
              void *p = chunk2mem (victim);//这里就和之前相同了将chunk指针转化为mem指针
              alloc_perturb (p, bytes);//默认没有做任何操作
              return p;
            }
    
          /* remove from unsorted list */
          unsorted_chunks (av)->bk = bck;
          bck->fd = unsorted_chunks (av);
    //将victim移除unsorted bin
          /* Take now instead of binning if exact fit */
    //下面是精确匹配,如果我们申请的大小转化后正好等于victim size,直接返回即可
          if (size == nb)
            {
              set_inuse_bit_at_offset (victim, size);
              //设置victim物理相邻的下一个chunk的prev_inuse位
              if (av != &main_arena)
                victim->size |= NON_MAIN_ARENA;
              //如果av不是main_arena 也就是说如果不是主进程,设置NON_MAIN_ARENA位
              check_malloced_chunk (av, victim, nb);//默认不做任何操作
              void *p = chunk2mem (victim);//这里就和之前相同了将chunk指针转化为mem指针
              alloc_perturb (p, bytes);//默认不做任何操作
              return p;
            }
    
          /* place chunk in bin */
    //若上一个步骤没有成功,则将victim置于对应的bin中
          if (in_smallbin_range (size))//如果victim的size大小为small bin
            {
              victim_index = smallbin_index (size);//获取大小对应的索引
              bck = bin_at (av, victim_index);//宏 bin_at(m, i) 通过 bin index 获得 bin 的链表头,m 指的是分配区,i 是索引
              fwd = bck->fd;//FIFO原则 bck->fd 指向最新插入的chunk(small bin为头插法)
            }
          else//若不在small bin的范围中,则
            {
              victim_index = largebin_index (size);
              bck = bin_at (av, victim_index);
              fwd = bck->fd;//FIFO原则 bck->fd 指向最新插入的chunk(small bin为头插法)
    
              /* maintain large bins in sorted order */
              if (fwd != bck)
                {
                  /* Or with inuse bit to speed comparisons */
                  size |= PREV_INUSE;
                  /* if smaller than smallest, bypass loop below */
                  assert ((bck->bk->size & NON_MAIN_ARENA) == 0);
                  if ((unsigned long) (size) < (unsigned long) (bck->bk->size))
                    {
                      fwd = bck;
                      bck = bck->bk;
    
                      victim->fd_nextsize = fwd->fd;
                      victim->bk_nextsize = fwd->fd->bk_nextsize;
                      fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
                    }
                  else
                    {
                      assert ((fwd->size & NON_MAIN_ARENA) == 0);
                      while ((unsigned long) size < fwd->size)
                        {
                          fwd = fwd->fd_nextsize;
                          assert ((fwd->size & NON_MAIN_ARENA) == 0);
                        }
    
                      if ((unsigned long) size == (unsigned long) fwd->size)
                        /* Always insert in the second position.  */
                        fwd = fwd->fd;
                      else
                        {
                          victim->fd_nextsize = fwd;
                          victim->bk_nextsize = fwd->bk_nextsize;
                          fwd->bk_nextsize = victim;
                          victim->bk_nextsize->fd_nextsize = victim;
                        }
                      bck = fwd->bk;
                    }
                }
              else
                victim->fd_nextsize = victim->bk_nextsize = victim;
            }
    
          mark_bin (av, victim_index);
          victim->bk = bck;
          victim->fd = fwd;
          fwd->bk = victim;
          bck->fd = victim;

#define MAX_ITERS       10000
          if (++iters >= MAX_ITERS)
            break;
        }

      /*
         If a large request, scan through the chunks of current bin in
         sorted order to find smallest that fits.  Use the skip list for this.
       */
    
      if (!in_smallbin_range (nb))
        {
          bin = bin_at (av, idx);
    
          /* skip scan if empty or largest chunk is too small */
          if ((victim = first (bin)) != bin &&
              (unsigned long) (victim->size) >= (unsigned long) (nb))
            {
              victim = victim->bk_nextsize;
              while (((unsigned long) (size = chunksize (victim)) <
                      (unsigned long) (nb)))
                victim = victim->bk_nextsize;
    
              /* Avoid removing the first entry for a size so that the skip
                 list does not have to be rerouted.  */
              if (victim != last (bin) && victim->size == victim->fd->size)
                victim = victim->fd;
    
              remainder_size = size - nb;
              unlink (av, victim, bck, fwd);
    
              /* Exhaust */
              if (remainder_size < MINSIZE)
                {
                  set_inuse_bit_at_offset (victim, size);
                  if (av != &main_arena)
                    victim->size |= NON_MAIN_ARENA;
                }
              /* Split */
              else
                {
                  remainder = chunk_at_offset (victim, nb);
                  /* We cannot assume the unsorted list is empty and therefore
                     have to perform a complete insert here.  */
                  bck = unsorted_chunks (av);
                  fwd = bck->fd;
      if (__glibc_unlikely (fwd->bk != bck))
                    {
                      errstr = "malloc(): corrupted unsorted chunks";
                      goto errout;
                    }
                  remainder->bk = bck;
                  remainder->fd = fwd;
                  bck->fd = remainder;
                  fwd->bk = remainder;
                  if (!in_smallbin_range (remainder_size))
                    {
                      remainder->fd_nextsize = NULL;
                      remainder->bk_nextsize = NULL;
                    }
                  set_head (victim, nb | PREV_INUSE |
                            (av != &main_arena ? NON_MAIN_ARENA : 0));
                  set_head (remainder, remainder_size | PREV_INUSE);
                  set_foot (remainder, remainder_size);
                }
              check_malloced_chunk (av, victim, nb);
              void *p = chunk2mem (victim);
              alloc_perturb (p, bytes);
              return p;
            }
        }
    
      /*
         Search for a chunk by scanning bins, starting with next largest
         bin. This search is strictly by best-fit; i.e., the smallest
         (with ties going to approximately the least recently used) chunk
         that fits is selected.
    
         The bitmap avoids needing to check that most blocks are nonempty.
         The particular case of skipping all bins during warm-up phases
         when no chunks have been returned yet is faster than it might look.
       */
    
      ++idx;
      bin = bin_at (av, idx);
      block = idx2block (idx);
      map = av->binmap[block];
      bit = idx2bit (idx);
    
      for (;; )
        {
          /* Skip rest of block if there are no more set bits in this block.  */
          if (bit > map || bit == 0)
            {
              do
                {
                  if (++block >= BINMAPSIZE) /* out of bins */
                    goto use_top;
                }
              while ((map = av->binmap[block]) == 0);
    
              bin = bin_at (av, (block << BINMAPSHIFT));
              bit = 1;
            }
    
          /* Advance to bin with set bit. There must be one. */
          while ((bit & map) == 0)
            {
              bin = next_bin (bin);
              bit <<= 1;
              assert (bit != 0);
            }
    
          /* Inspect the bin. It is likely to be non-empty */
          victim = last (bin);
    
          /*  If a false alarm (empty bin), clear the bit. */
          if (victim == bin)
            {
              av->binmap[block] = map &= ~bit; /* Write through */
              bin = next_bin (bin);
              bit <<= 1;
            }
    
          else
            {
              size = chunksize (victim);
    
              /*  We know the first chunk in this bin is big enough to use. */
              assert ((unsigned long) (size) >= (unsigned long) (nb));
    
              remainder_size = size - nb;
    
              /* unlink */
              unlink (av, victim, bck, fwd);
    
              /* Exhaust */
              if (remainder_size < MINSIZE)
                {
                  set_inuse_bit_at_offset (victim, size);
                  if (av != &main_arena)
                    victim->size |= NON_MAIN_ARENA;
                }
    
              /* Split */
              else
                {
                  remainder = chunk_at_offset (victim, nb);
    
                  /* We cannot assume the unsorted list is empty and therefore
                     have to perform a complete insert here.  */
                  bck = unsorted_chunks (av);
                  fwd = bck->fd;
      if (__glibc_unlikely (fwd->bk != bck))
                    {
                      errstr = "malloc(): corrupted unsorted chunks 2";
                      goto errout;
                    }
                  remainder->bk = bck;
                  remainder->fd = fwd;
                  bck->fd = remainder;
                  fwd->bk = remainder;
    
                  /* advertise as last remainder */
                  if (in_smallbin_range (nb))
                    av->last_remainder = remainder;
                  if (!in_smallbin_range (remainder_size))
                    {
                      remainder->fd_nextsize = NULL;
                      remainder->bk_nextsize = NULL;
                    }
                  set_head (victim, nb | PREV_INUSE |
                            (av != &main_arena ? NON_MAIN_ARENA : 0));
                  set_head (remainder, remainder_size | PREV_INUSE);
                  set_foot (remainder, remainder_size);
                }
              check_malloced_chunk (av, victim, nb);
              void *p = chunk2mem (victim);
              alloc_perturb (p, bytes);
              return p;
            }
        }
    
    use_top:
      /*
         If large enough, split off the chunk bordering the end of memory
         (held in av->top). Note that this is in accord with the best-fit
         search rule.  In effect, av->top is treated as larger (and thus
         less well fitting) than any other available chunk since it can
         be extended to be as large as necessary (up to system
         limitations).
    
         We require that av->top always exists (i.e., has size >=
         MINSIZE) after initialization, so if it would otherwise be
         exhausted by current request, it is replenished. (The main
         reason for ensuring it exists is that we may need MINSIZE space
         to put in fenceposts in sysmalloc.)
       */
    
      victim = av->top;
      size = chunksize (victim);
    
      if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE))
        {
          remainder_size = size - nb;
          remainder = chunk_at_offset (victim, nb);
          av->top = remainder;
          set_head (victim, nb | PREV_INUSE |
                    (av != &main_arena ? NON_MAIN_ARENA : 0));
          set_head (remainder, remainder_size | PREV_INUSE);
    
          check_malloced_chunk (av, victim, nb);
          void *p = chunk2mem (victim);
          alloc_perturb (p, bytes);
          return p;
        }
    
      /* When we are using atomic ops to free fast chunks we can get
         here for all block sizes.  */
      else if (have_fastchunks (av))
        {
          malloc_consolidate (av);
          /* restore original bin index */
          if (in_smallbin_range (nb))
            idx = smallbin_index (nb);
          else
            idx = largebin_index (nb);
        }
    
      /*
         Otherwise, relay to handle system-dependent cases
       */
      else
        {
          void *p = sysmalloc (nb, av);
          if (p != NULL)
            alloc_perturb (p, bytes);
          return p;
        }
    }

}

来源:freebuf.com 2021-02-23 14:37:39 by: summerN

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论