admin管理员组

文章数量:1576786

目录

一、smallbins的定义和空闲链表的使用条件

二、smallbins的具体实现

三、malloc_consolidate整理操作

四、unsorted bin的具体实现


前一章,我们讲解了fastbins的空闲链表的分配逻辑。这一章,我们主要讲一下smallbins和unsorted bin的分配逻辑。

一、smallbins的定义和空闲链表的使用条件


smallbins的定义:

  • smallbins放置在malloc_state-> bins数组上,数组从2开始编号到63,前62个bin为small bins
  • 小于512字节(64位机器1024字节)的chunk被称为small chunk,而保存small chunks的bin被称为small bin
  • small bin每个bin之间相差8个字节64位16字节),同一个small bin中的chunk具有相同大小。起始bin大小为16字节(64位系统32)。

smallbins空闲链表的使用条件:

  • 需要符合smallbin的大小,并且空闲链表上正好有空闲的chunk,则可以分配
  • 数组对应的chunk链表,默认情况下需要有2个及以上的元素,才能分配。如果只有一个chunk,则这个chunk是用于管理和连接链表的

二、smallbins的具体实现


具体的实现步骤如下:

  1. 如果符合smallbin的大小,则从smallbin的数组上获取一个chunk进行内存分配

  2. 如果是smallbin,则通过bins的数组下标获取到对应的chunk双向链表

  3. 如果空闲链表中有空闲的chunk,然后从链表的尾部通过弹出一个chunk,并修改双向链表

  4. 如果空闲链表中没有空闲的chunk,则此次分配失败,需要跳出smallbins上的分配逻辑,往下走其他逻辑的分配方式

  5. 获取得到的victim就是要操作的内存chunk对象,通过chunk2mem和alloc_perturb函数,初始化对象

 /*
   * 如果符合smallbin的大小,则从smallbin的数组上获取一个chunk进行内存分配,如果存在空闲chunk则分配成功;如果空闲链表为空,则分配失败需要往下走
   * 1. 如果是smallbin,则通过bins的数组下标获取到对应的chunk双向链表
   * 2. 然后从链表的尾部通过弹出一个chunk,并修改双向链表
   * 3. 获取得到的victim就是要操作的内存chunk对象,通过chunk2mem和alloc_perturb函数,初始化对象 */
  if (in_smallbin_range (nb))
    {
      idx = smallbin_index (nb); //获取smallbin的数组下标
      bin = bin_at (av, idx); //av->获取数组上的bin ; bins数组为:mchunkptr结构的数组

      //只有bin这个chunk双向链表有2个以上值,才会分配
      //bin是mchunkptr结构的链表(双向链表);last(bin) 获取bin->bk,获取最后一个chunk;从链表的尾部,取一个victim
      //Case1: A -> B -> C -> D 四个chunk,则取出D,调整双向链表:A -> B -> C
      //Case2: A -> B 两个chunk,则取出B,调整双向链表:A->bk = A / A->fd = A
      if ((victim = last (bin)) != bin)
        {
          bck = victim->bk; //获取victim的前一个bin
	  if (__glibc_unlikely (bck->fd != victim))
	    malloc_printerr ("malloc(): smallbin double linked list corrupted");
          set_inuse_bit_at_offset (victim, nb); //将victim设置为已使用状态
          bin->bk = bck; //将bin->bk设置为bck
          bck->fd = bin; //将bck->fd设置为bin

          if (av != &main_arena)
	    set_non_main_arena (victim);
          check_malloced_chunk (av, victim, nb);
 
..................简

          void *p = chunk2mem (victim); //通过chunk结构,获取data数据部分的指针地址
          alloc_perturb (p, bytes); //初始化内存块
          return p;
        }
    }

三、malloc_consolidate整理操作


 如果是largebin,则会把fast bin中的chunk进行一次整理合并。然后将合并后的chunk放入unsorted bin中,这是通过malloc_consolidate这个函数完成。核心逻辑就是进行一次碎片化整理。

malloc_consolidate具体不展开了,核心处理逻辑:

  1. 遍历fastbins,针对里面的chunk进行一次循环遍历操作,检查每个chunk的前一个和后一个chunk是否是free状态,如果是free状态,则合并
  2. 如果合并后的chunk不和top chunk挨着,则将这个chunk放进unsorted bins中
  3. 如果合并后的chunk和top chunk挨着,则重新设置top chunk的起始位置
  /*
     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
    {
	  /* 如果是largebin,则会把fast bin中的chunk进行一次整理合并
	   * 然后将合并后的chunk放入unsorted bin中,这是通过malloc_consolidate这个函数完成*/
      idx = largebin_index (nb);
      if (atomic_load_relaxed (&av->have_fastchunks))
        malloc_consolidate (av);
    }

四、unsorted bin的具体实现


unsorted bin的定义:

  • 是bins的一个缓冲区,bins数组下标为1的即是unstored bin,被释放后未存储的bin
  • 当用户释放的内存大于max_fast或者fast bins合并后的chunk都会进入unsorted bin上
  • 当用户malloc的时候,如果fastbins和smallbins没有分配成功,则会到unsorted bin上查找是否有合适的bin,如果没有合适的bin,ptmalloc会将unsorted bin上的chunk放入bins上,然后到bins上查找合适的空闲chunk。

核心处理流程:

  1. 从unsorted bin的双向链表上去循环遍历每一个未存储的chunk,进行内存分配或者重新放置到smallbins和largebins上去

  2. 如果,我们分配的是一个small类型的,遇到合适大小的chunk,则可以将chunk进行切割,并且切割后返回分配的内存chunk

  3. 如果,请求的大小,正好和chunk大小匹配,则直接分配成功

  4. 如果,上面两步的分配都不成功,则将该chunk根据大小类型,放置到smallbins或者largebins上去

  5. 最后,继续开始循环迭代操作

  //尝试从unsorted bin中分配
  for (;; )
    {
      int iters = 0;
      //从unsorted bin的双向链表上去循环处理,直到循环回到起始节点
      //unsorted bin放置在av->bins[1]的数组上,也是chunk类型的双向链表
      while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
        {
          bck = victim->bk; //获取前一个chunk
          //确认当前chunk的大小,因为unsorted bin放置的是整理后的bin,大小并不是由small bin和large bin这种数组下标来决定的
          size = chunksize (victim);
          mchunkptr next = chunk_at_offset (victim, size); //获取内存块上相邻的下一个chunk的地址,这个不是双向链表上的,是内存相邻的chunk

.............简

          /*
           * 分配的是small类型的并且小于当前的chunk,则可以进行切割分配
           * 几个条件:
           * 1. 需要符合smallbin
           * 2. unsorted bins上只剩下一个bin
           * 3. 当前的chunk等于last_remainder
           * 4. size需要大于nb,并且分割后的last_remainder,仍然可以使用*/
          if (in_smallbin_range (nb) &&
              bck == unsorted_chunks (av) &&
              victim == av->last_remainder &&
              (unsigned long) (size) > (unsigned long) (nb + MINSIZE))
            {
              /* split and reattach remainder 进行chunk的切割操作 */
              remainder_size = size - nb; //remainder的大小
              remainder = chunk_at_offset (victim, nb); //remainder指针地址
              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)); //设置mchunk_size 大小
              set_head (remainder, remainder_size | PREV_INUSE); //设置mchunk_size 大小
              set_foot (remainder, remainder_size); //设置下一个chunk->mchunk_prev_size

              check_malloced_chunk (av, victim, nb);
              void *p = chunk2mem (victim); //通过chunk结构,获取data数据部分的指针地址
              alloc_perturb (p, bytes); //初始化内存块
              return p;
            }

          /* remove from unsorted list */
          if (__glibc_unlikely (bck->fd != victim))
            malloc_printerr ("malloc(): corrupted unsorted chunks 3");
          unsorted_chunks (av)->bk = bck;
          bck->fd = unsorted_chunks (av);

          /* Take now instead of binning if exact fit */
          /* 请求的大小,正好和chunk大小匹配,则直接分配成功 */
          if (size == nb)
            {
              set_inuse_bit_at_offset (victim, size); //将victim设置为已使用状态
              if (av != &main_arena)
		set_non_main_arena (victim);
............简
              check_malloced_chunk (av, victim, nb);
              void *p = chunk2mem (victim);//通过chunk结构,获取data数据部分的指针地址
              alloc_perturb (p, bytes);//初始化内存块
              return p;

            }

          /* place chunk in bin */
          //当前chunk属于small bin的范围,将其放回small bin
          if (in_smallbin_range (size))
            {
              victim_index = smallbin_index (size);
              bck = bin_at (av, victim_index);
              fwd = bck->fd;
            }
          else //当前chunk属于large bin的范围,将其放回large bin
            {
              victim_index = largebin_index (size);
              bck = bin_at (av, victim_index);
              fwd = bck->fd;

          ................简

          mark_bin (av, victim_index);
          // bck -> fwd 插入victim   bck -> victim -> fwd
          victim->bk = bck;
          victim->fd = fwd;
          fwd->bk = victim;
          bck->fd = victim;



#define MAX_ITERS       10000
          //循环最多10000次,则结束
          if (++iters >= MAX_ITERS)
            break;
        }

下一章节,我们继续看_int_malloc函数里面的largebins和topchunk的实现

本文标签: 函数源码intmallocptmallocBin