glibc-2.23 _int_malloc函数流程分析

_int_malloc流程分析

1.尝试在fastbin里面寻找

若needbytes 是小于fastbin最大chunk的大小,根据needbytes计算出idx,在对应的bin里面看一下有没有chunk。若对应的bin并不是空的,那么取出一个chunk 名为victim,检查一下victim的size是否与所在的bin对应,然后返回。否则往下执行
对应代码:

if ((unsigned long)(nb) <= (unsigned long)(get_max_fast()))
 {
   idx = fastbin_index(nb);
   mfastbinptr *fb = &fastbin(av, idx);
   mchunkptr pp = *fb;
   do
   {
     victim = pp;
     if (victim == NULL)
       break;
   } while ((pp = catomic_compare_and_exchange_val_acq(fb, victim->fd, victim)) != victim);
   //检测目标chunk的size是否正确.
   if (victim != 0)
   {
     if (__builtin_expect(fastbin_index(chunksize(victim)) != idx, 0))
     {
       errstr = "malloc(): memory corruption (fast)";
     errout:
       malloc_printerr(check_action, errstr, chunk2mem(victim), av);
       return NULL;
     }
     check_remalloced_chunk(av, victim, nb);
     void *p = chunk2mem(victim);
     alloc_perturb(p, bytes);
     return p;
   }
 }

2.尝试在small bin里面寻找

若needbytes是属于small bin范围,那么根据needbytes计算出idx,看一下对应的bin里面是否有chunk,若有的话,取出最后一个chunk,返回。否则往下执行。
对应代码:

if (in_smallbin_range(nb))
 {
   //0x20,0x30.......0x410
   idx = smallbin_index(nb);
   bin = bin_at(av, idx);
   if ((victim = last(bin)) != bin)
   {
     //一开始每一个Bin里面的地址都是0,得进行初始化.
     if (victim == 0) /* initialization check */
       malloc_consolidate(av);
     else
     {
       bck = victim->bk;
       if (__glibc_unlikely(bck->fd != victim))
       {
         errstr = "malloc(): smallbin double linked list corrupted";
         goto errout;
       }
       set_inuse_bit_at_offset(victim, nb);
       bin->bk = bck;
       bck->fd = bin;

       if (av != &main_arena)
         victim->size |= NON_MAIN_ARENA;
       check_malloced_chunk(av, victim, nb);
       void *p = chunk2mem(victim);
       alloc_perturb(p, bytes);
       return p;
     }
   }
 }

3.触发malloc_consolidate

若needbytes不属于small bin,那么会触发malloc_consolidate来合并fastbin里面的chunk。
由于fastbin里面的chunk始终被认为是inuse,所以fastbin里面的chunk除了malloc_consolidate函数能够将其合并之外,没有其他的机会。合并的原因就是(可能)提供所需的large chunk。
对应代码:

 else
 {
     idx = largebin_index(nb);
     //由于fastbin里面的chunk始终是被认为使用的,所以不会被合并,
     //但是现在需要large 的chunk,得合并fastbin里面的chunk,可能提供large chunk
     if (have_fastchunks(av))
       malloc_consolidate(av);
 }

4.for()循环:

  1. 从后往前遍历unsorted bin里面的chunk:
    • 若unsorted bin里面只有一个chunk,满足need bytes 是small bin范围内,这个chunk 是reminder,reminder除去needbytes之任能形成一个chunk,那么就从这个chunk上分割出需要的大小,剩余部分仍然保留在unsorted bin里面,reminder指向剩余的部分
if (in_smallbin_range(nb) &&bck == unsorted_chunks(av) &&victim == av->last_remainder &&
            (unsigned long)(size) > (unsigned long)(nb + MINSIZE))
          {
              /* split and reattach remainder */
              remainder_size = size - nb;
              remainder = chunk_at_offset(victim, nb);
              //剩余的部分继续放到unsorted bin里面
              unsorted_chunks(av)->bk = unsorted_chunks(av)->fd = remainder;
              av->last_remainder = remainder;
              remainder->bk = remainder->fd = unsorted_chunks(av);
              //
              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;
          }
    • 将chunk从unsorted bin里面摘除
 unsorted_chunks(av)->bk = bck;
 bck->fd = unsorted_chunks(av);
    • 若chunk size 和need bytes相等,返回这个chunk
 if (size == nb)
 {
   set_inuse_bit_at_offset(victim, size);
   if (av != &main_arena)
     victim->size |= NON_MAIN_ARENA;
   check_malloced_chunk(av, victim, nb);
   void *p = chunk2mem(victim);
   alloc_perturb(p, bytes);
   return p;
 }
    • 将该chunk 放到对应的normal bin里面,并标记bitmap
if (in_smallbin_range(size))
{
  victim_index = smallbin_index(size);

  //bck 指向的是数组
  //fwd 是next 
  bck = bin_at(av, victim_index);
  fwd = bck->fd;
}
else
{
  victim_index = largebin_index(size);
  bck = bin_at(av, victim_index);
  fwd = bck->fd;

  /* 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);
    //新的chunk比最小的chun大小还要小,就把新的这个chunk放在large bin的末尾.
    if ((unsigned long)(size) < (unsigned long)(bck->bk->size))
    {
      fwd = bck;
      bck = bck->bk;
      //设置victim 的fd_nextsize和bk_nextsize
      victim->fd_nextsize = fwd->fd;
      victim->bk_nextsize = fwd->fd->bk_nextsize;
      //设置victim 下一个chunk (head->fd)的bk__nextsize和前一个chunk的fd_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);
      }
      //victim 和fwd 一样大
      if ((unsigned long)size == (unsigned long)fwd->size)
        /* Always insert in the second position.  */
        fwd = fwd->fd;
      else
      {
        //victim 比fwd 更大.
        victim->fd_nextsize = fwd;
        victim->bk_nextsize = fwd->bk_nextsize;
        //
        //后一个nextsize chunk
        fwd->bk_nextsize = victim;
        //前一个nextsize chunk
        victim->bk_nextsize->fd_nextsize = victim;
      }
      bck = fwd->bk;
    }
  }
  else
    //对应的large bin是空的
    victim->fd_nextsize = victim->bk_nextsize = victim;
}
//在bitmap里面标记这个bin不是空的.
mark_bin(av, victim_index);

victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;
  1. 若need bytes不属于small bin范围,即在large chunk范围内:
    • 找到对应bin
    • 若对应的bin不为空,而且在这个bin里面最小的chunk(也就是最后一个chunk) 的chunk size >= needbytes,从后往前遍历,寻找到第一个>= needbytes的chunk,然后unlink,在这个操作中会避免取下每种size的第一个chunk(因为还得调整bk_nextsize和fd_nextsize)。取下之后在这个chunk上分割出需要的大小,并把剩余的部分插到unsorted bin的开头,返回
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;
  //从后往前遍历..寻找第一个 >= nb的chunk
  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
  {
    //把reminder 插入到unsorted bin的开头位置
    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;
}
  1. 扫描bins找到能提供needbytes的chunk
    开头有这样一段代码:
++idx;                        
bin = bin_at(av, idx);
block = idx2block(idx);     
map = av->binmap[block];
bit = idx2bit(idx);       

如果能执行到这里,说明前面的代码在idx对应的bin里面没有找到能提供needbytes的chunk,这里idx++,接下来就从下一个bin开始寻找。
(这里是通过bitmap来遍历bin,没啥复杂的,详细的看代码就行)
for():

    • 从下一个bin开始,通过bitmap找到能提供need bytes的bin,若没有找到,执行use_top
 if (bit > map || bit == 0)
 {
   //先找到能满足需求的 binmap,
   do
   {
     if (++block >= BINMAPSIZE) /* out of bins */
       goto use_top;
   } while ((map = av->binmap[block]) == 0);

   //bin对应第一个
   bin = bin_at(av, (block << BINMAPSHIFT));
   bit = 1;
 }
 //执行到这里就一定有更大的chunk,
 //检查当前的block里面是否有大的bin
 /* Advance to bin with set bit. There must be one. */
 while ((bit & map) == 0)
 {
   bin = next_bin(bin);
   bit <<= 1;
   assert(bit != 0);
 }
    • 若bin为空,进行下一次循环
/* 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;
}
    • 不为空的话,就在最后一个chunk (large bin里面这个是最小的chunk,但是足够了)划分出需要的内存,剩余的部分做出相应的处理
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;
}

5 use top

  1. 若top chunk能提供所需的大小,从top chunk上分割出需要的内存,返回。
  2. 否则 若有fastbin的话,那么合并fastbin里面的chunk
  3. 若没有fastbin,通过sysmalloc 分配内存,返回

其他部分:

1.关于last_reminder

  /* The remainder from the most recent split of a small request */
  mchunkptr last_remainder;

这个指针指向的是“最近的某个 small request划分剩下的chunk”,然后大家应该就知道了前面的_int_malloc中什么时候划分之后需要把last_reminder指向剩下的chunk。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Suspend.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值