admin管理员组

文章数量:1530925

目录

一、sysmalloc函数基本分配逻辑

二、强制try_mmap分配方式

三、非主分配区分配的实现

1. 设置老的Top chunk的参数

2. 尝试使用grow_heap函数

3. 尝试使用new_heap函数

4. 尝试使用try_mmap方式

四、主分配区分配的实现

1. 设置Top扩容的size值

2. brk分配成功的方式

3. brk分配失败采用MMAP分配

五、主分配区分配成功后对齐裁剪操作

1. 内存地址连续相邻直接扩容Top

2. brk方式分配地址不相邻情况

3. mmap方式分配地址不相邻情况

4. 设置调整后的Topchunk

六、切割新的Topchunk分配内存


前面三章,我们讲解了malloc的核心分配函数_int_malloc。_int_malloc核心是从我们管理的bins上去寻找空闲的chunk,并分配内存。如果没有空闲的内存,则到Top chunk上去分配。如果Top chunk也无法满足分配的场景,则需要调用sysmalloc进行内存的分配。

本章节,我们重点讲sysmalloc的具体实现。

一、sysmalloc函数基本分配逻辑


sysmalloc定义:当Top chunk内存空间不足的时候,就会调用sysmalloc函数进行内存分配操作。

参数:入参nb,为需要分配的;av为分配区状态机对象

/*
   sysmalloc handles malloc cases requiring more memory from the system.
   On entry, it is assumed that av->top does not have enough
   space to service request for nb bytes, thus requiring that av->top
   be extended or replaced.
   调用系统分配函数:sysmalloc
   说明:进入sysmalloc则表示 top chunk的空间不足了,需要进行扩容av->top
   nb:请求的内存大小
   mstate:内存分配状态机(分配区)
 */

static void *
sysmalloc (INTERNAL_SIZE_T nb, mstate av)
{
  mchunkptr old_top;              /* 老的Topchunk的地址指针  mstate->top incoming value of av->top */
  INTERNAL_SIZE_T old_size;       /* 老的Top chunk的大小 its size */
  char *old_end;                  /* 老的Top chunk的尾部地址 its end address */

  long size;                      /* 第一次分配的内存size arg to first MORECORE or mmap call */
  char *brk;                      /* 通过brk分配后返回的对象 return value from MORECORE */

  long correction;                /* 用于记录第二次分配的值 arg to 2nd MORECORE call */
  char *snd_brk;                  /* 第二次处理后返回的值(也是第一次的尾部) 2nd return val */

  INTERNAL_SIZE_T front_misalign; /* 不可用的新空间的头部字节 unusable bytes at front of new space */
  INTERNAL_SIZE_T end_misalign;   /* 新内存块尾部对齐的字节 partial page left at end of new space */
  char *aligned_brk;              /* 对齐后的brk值 aligned offset into brk */

  mchunkptr p;                    /* 返回的结果p the allocated/returned chunk */
  mchunkptr remainder;            /* Top chunk切割后剩余的remainder chunk remainder from allocation */
  unsigned long remainder_size;   /* Top chunk切割后剩余的remainder chunk 的size its size */


  size_t pagesize = GLRO (dl_pagesize);
  bool tried_mmap = false;

sysmalloc基本逻辑:

  1. 非主分配区,通过MMAP并生成heap对象进行Top chunk的扩容操作。
  2. 非主分配区使用MMAP分配,32位操作系统每次分配1M,64位系统每次分配64M
  3. 非主分配区如果分配失败,则调用try_mmap,直接通过MMAP返回需要的内存
  4. 主分配区里面,一般情况下调用brk对Top chunk进行扩容
  5. 主分配区如果通过brk分配失败,则才调用MMAP方式分配
  6. 主分配区brk分配,每次除了需要分配的nb内存字节外,额外还会扩容mp_.top_pad=128K的内存空间;如果主分配区使用MMAP分配,每次分配1M+
  7. 无论主分配区还是非主分配区,分配成功后,都要对分配的内存进行一系列对齐、标记、裁剪、释放等操作
  8. 最后,切割当前需要分配的内存,并将Remainder chunk指向Top chunk地址

二、强制try_mmap分配方式


try_mmap是尝试直接进行mmap的方式分配一段内存。当非主分配区通过扩容heap或者new heap都失败的情况下,采用try_mmap方式分配。

但是需要符合MMAP的阀值以及系统支持MMAP,nb分配的内存为大对象(超过128k)。该场景则直接返回MMAP分配的内存chunk,不调整Top chunk。

如果MMAP方式分配成功,对分配的内存进行对齐操作,需要计算偏移量,并标记该内存是IS_MMAPPED类型的。最后直接返回内存地址。

  /*
     If have mmap, and the request size meets the mmap threshold, and
     the system supports mmap, and there are few enough currently
     allocated mmapped regions, try to directly map this request
     rather than expanding top.
     1. av==NULL,则直接采用MMAP的方式分配内存
     2. nb分配的内存为大对象(超过128k),并且符合MMAP的阀值以及系统支持MMAP,则采用MMAP分配
     mp_.n_mmaps_max=65536
     mp_.mmap_threshold=128*1024

     需要goto try_mmap,才会进入MMAP分配逻辑,该场景则直接返回MMAP分配的内存chunk,不调整Top chunk
   */

  if (av == NULL
      || ((unsigned long) (nb) >= (unsigned long) (mp_.mmap_threshold)
	  && (mp_.n_mmaps < mp_.n_mmaps_max)))
    {
      char *mm;           /* MMAP的返回值 return value from mmap call*/

      /* 尝试进行MMAP分配 */
    try_mmap:
      /*
         Round up size to nearest page.  For mmapped chunks, the overhead
         is one SIZE_SZ unit larger than for normal chunks, because there
         is no following chunk whose prev_size field could be used.

         See the front_misalign handling below, for glibc there is no
         need for further alignments unless we have have high alignment.
       */
	  /* 进行size的内存对齐操作 */
      if (MALLOC_ALIGNMENT == 2 * SIZE_SZ)
        size = ALIGN_UP (nb + SIZE_SZ, pagesize); //调整size
      else
        size = ALIGN_UP (nb + SIZE_SZ + MALLOC_ALIGN_MASK, pagesize);//调整size
      tried_mmap = true;

      /* Don't try if size wraps around 0 */
      if ((unsigned long) (size) > (unsigned long) (nb))
        {
    	  /* 直接调用MMAP映射一块内存,大小为size,可读可写*/
          mm = (char *) (MMAP (0, size, PROT_READ | PROT_WRITE, 0)); //调用MMAP的方式,获取一块size大小的内存

          /* 如果分配成功 */
          if (mm != MAP_FAILED)
            {
              /*
                 The offset to the start of the mmapped region is stored
                 in the prev_size field of the chunk. This allows us to adjust
                 returned start address to meet alignment requirements here
                 and in memalign(), and still be able to compute proper
                 address argument for later munmap in free() and realloc().
               */

        	  /* 分配的mm内存地址需要进行内存对齐,front_misalign表示对齐的地址前有多少个可以对齐的字节*/
              if (MALLOC_ALIGNMENT == 2 * SIZE_SZ)
                {
                  /* For glibc, chunk2mem increases the address by 2*SIZE_SZ and
                     MALLOC_ALIGN_MASK is 2*SIZE_SZ-1.  Each mmap'ed area is page
                     aligned and therefore definitely MALLOC_ALIGN_MASK-aligned.  */
                  assert (((INTERNAL_SIZE_T) chunk2mem (mm) & MALLOC_ALIGN_MASK) == 0);
                  front_misalign = 0;
                }
              else
                front_misalign = (INTERNAL_SIZE_T) chunk2mem (mm) & MALLOC_ALIGN_MASK;

              /* 如果有可以对齐的字节 */
              if (front_misalign > 0)
                {

                  correction = MALLOC_ALIGNMENT - front_misalign; //计算偏移量
                  p = (mchunkptr) (mm + correction); //mm+偏移量后,得到最终返回的chunk的地址
                  //说白了对齐后,将便宜部分的字节当成了一个新的chunk结构,free的时候要一起清理
                  set_prev_size (p, correction); //设置前一个p->mchunk_prev_size;
                  set_head (p, (size - correction) | IS_MMAPPED); //设置p->mchunk_size,并且需要减去偏移量 (size + MMAP映射)
                }
              else
                {
            	  /* 这里不需要对齐操作*/
                  p = (mchunkptr) mm; //获取得到chunk的指针地址
                  set_prev_size (p, 0); //这里没有偏移,所以设置前一个mchunk_prev_size为0
                  set_head (p, size | IS_MMAPPED); //设置p->mchunk_size (size + MMAP映射)
                }

              /* update statistics 更新各种状态*/

              int new = atomic_exchange_and_add (&mp_.n_mmaps, 1) + 1;
              atomic_max (&mp_.max_n_mmaps, new);

              unsigned long sum;
              sum = atomic_exchange_and_add (&mp_.mmapped_mem, size) + size;
              atomic_max (&mp_.max_mmapped_mem, sum);

              check_chunk (av, p);

              return chunk2mem (p); //这里返回内存地址,非chunk地址
            }
        }
    }

三、非主分配区分配的实现


如果是非主分配区,则通过heap_info方式获取堆信息结构,并且通过MMAP分配内存。

1. 设置老的Top chunk的参数


brk和snd_brk默认值都设置为失败。brk为第一次分配后的指针地址;snd_brk为第二次分配的地址,同时也是第一次分配内存的尾部地址。

  /* There are no usable arenas and mmap also failed.  */
  /* 如果分配区为空,则不能分配,返回 */
  if (av == NULL)
    return 0;

  /* Record incoming configuration of top */
  /* 获取top 的 顶部信息*/
  old_top = av->top; //老的Topchunk的地址
  old_size = chunksize (old_top); //老的Topchunk的size
  old_end = (char *) (chunk_at_offset (old_top, old_size)); //老的Topchunk的尾部地址

  /* brk=第一次分配返回值 ;snd_brk=第二次分配返回值 */
  brk = snd_brk = (char *) (MORECORE_FAILURE); //默认设置分配失败

2. 尝试使用grow_heap函数


如果剩余的空间不足,首先通过grow_heap尝试heap区的扩容。

grow_heap函数进行扩容,有分配页大小的限制,分配页的大小要小于HEAP_MAX_SIZE。

/* 如果不是主分配区,则通过heap_info方式获取堆信息结构,并且通过MMAP分配内存 */
  if (av != &main_arena)
    {
      heap_info *old_heap, *heap;
      size_t old_heap_size;

      /* First try to extend the current heap. */
      /* 通过heap_for_ptr,获取当前的heap的数据结构 */
      old_heap = heap_for_ptr (old_top);
      old_heap_size = old_heap->size;


      /**
       * 如果剩余的空间不足,首先通过grow_heap尝试heap区的扩容
       * grow_heap函数进行扩容,有分配页大小的限制,分配页的大小要小于HEAP_MAX_SIZE
       */
      if ((long) (MINSIZE + nb - old_size) > 0
          && grow_heap (old_heap, MINSIZE + nb - old_size) == 0)
        {
    	  /* 扩容方式成功 */
          av->system_mem += old_heap->size - old_heap_size; //变更系统内存记录,增加部分为新的heap的size减去老的heap的值
          set_head (old_top, (((char *) old_heap + old_heap->size) - (char *) old_top)
                    | PREV_INUSE); //设置p->mchunk_size (size + 使用中)
        }

3. 尝试使用new_heap函数


扩容失败,则new一个新的heap ,并将av->top指向到新的heap

new_heap 通过MMAP方式分配一块内存, 32位系统每次映射1M,64位系统每次映射64M

      /* 扩容失败,则new一个新的heap ;并将av->top指向到新的heap */
      /* new_heap 通过MMAP方式分配一块内存, 32位系统每次映射1M,64位系统每次映射64M*/
      else if ((heap = new_heap (nb + (MINSIZE + sizeof (*heap)), mp_.top_pad)))
        {
          /* Use a newly allocated heap.  */
          heap->ar_ptr = av; //新的heap指向av
          heap->prev = old_heap; //指向前一个老的heap
          av->system_mem += heap->size; //调整分配区的系统内存
          /* Set up the new top.  */
          /* #define top(ar_ptr) ((ar_ptr)->top) */
          top (av) = chunk_at_offset (heap, sizeof (*heap)); //减去heap结构的长度,就能定位到chunk的偏移量,并将new_heap出来的块放置到av->top上
          set_head (top (av), (heap->size - sizeof (*heap)) | PREV_INUSE); //设置p->mchunk_size

          /* Setup fencepost and free the old top chunk with a multiple of
             MALLOC_ALIGNMENT in size. */
          /* The fencepost takes at least MINSIZE bytes, because it might
             become the top chunk again later.  Note that a footer is set
             up, too, although the chunk is marked in use. */
          old_size = (old_size - MINSIZE) & ~MALLOC_ALIGN_MASK;
          set_head (chunk_at_offset (old_top, old_size + 2 * SIZE_SZ), 0 | PREV_INUSE);//设置p->mchunk_size (size + PREV使用中)
          if (old_size >= MINSIZE)
            {
              set_head (chunk_at_offset (old_top, old_size), (2 * SIZE_SZ) | PREV_INUSE);//设置p->mchunk_size (size + PREV使用中)
              set_foot (chunk_at_offset (old_top, old_size), (2 * SIZE_SZ)); //设置p->mchunk_prev_size
              set_head (old_top, old_size | PREV_INUSE | NON_MAIN_ARENA);//设置p->mchunk_size (size + PREV使用中 + 非主分配区)
              _int_free (av, old_top, 1); //释放old_top
            }
          else
            {
              set_head (old_top, (old_size + 2 * SIZE_SZ) | PREV_INUSE);//设置p->mchunk_size (size + PREV使用中)
              set_foot (old_top, (old_size + 2 * SIZE_SZ));//设置p->mchunk_prev_size
            }
        }

4. 尝试使用try_mmap方式


      /* 直接跳转到try_mmap,通过MMAP分配一块内存 */
      else if (!tried_mmap)
        /* We can at least try to use to mmap memory.  */
        goto try_mmap;
    }

四、主分配区分配的实现


 如果是主分配区,则直接扩容Top chunk(brk和mmap方式都有)。

1. 设置Top扩容的size值


size 需要等于 nb(此次分配的容量) + top_pad(每次分配扩展值128K) + MINSIZE对齐字节

如果主分配区都是通过brk方式分配的,是连续性分配的,可以减去老的Top chunk剩余的old_size值。因为nb大于old_size。size也需要按照pagesize进行页对齐。

  else     /* av == main_arena  如果是主分配区,则直接扩容Top chunk(brk和mmap方式都有)*/


    { /* Request enough space for nb + pad + overhead */
	  //# define DEFAULT_TOP_PAD 131072 = 128*1024  mp_.top_pad默认值128K
	  //mp_.top_pad 初始化或扩展堆的时候需要多申请的内存大小
      size = nb + mp_.top_pad + MINSIZE; //size=分配的内存需要加上 对齐的一些字节;

      /*
         If contiguous, we can subtract out existing space that we hope to
         combine with new space. We add it back later only if
         we don't actually get contiguous space.
         contiguous:用于判断是否为连续brk分配,主分配区先减去已经存在的top空间,再向操作系统申请
         如果是连续brk分配,可以减去原有的old_size
         如果是非连续brk分配,后续还会降old_size加回去
         由于old_size小于nb,top_pad又是128K,所以减去之后也有足够的空间存储
       */
      if (contiguous (av))
        size -= old_size;

      /*
         Round to a multiple of page size.
         If MORECORE is not contiguous, this ensures that we only call it
         with whole-page arguments.  And if MORECORE is contiguous and
         this is not first time through, this preserves page-alignment of
         previous calls. Otherwise, we correct to page-align below.
       */
      //按照页进行对齐 对齐
      size = ALIGN_UP (size, pagesize);

2. brk分配成功的方式


调用MORECORE进行brk的分配。

     //size大于0,然后通过系统调用(sbrk)分配size大小的内存
      if (size > 0)
        {
          brk = (char *) (MORECORE (size));
          LIBC_PROBE (memory_sbrk_more, 2, brk, size);
        }

      /* 如果分配成功 */
      if (brk != (char *) (MORECORE_FAILURE))
        {
          /* Call the `morecore' hook if necessary.  */
    	  //分配成功,进行原子操作,调用__after_morecore_hook
          void (*hook) (void) = atomic_forced_read (__after_morecore_hook);
          if (__builtin_expect (hook != NULL, 0))
            (*hook)();
        }

3. brk分配失败采用MMAP分配


进入mmap之后,相当于之前的brk被打断了,则此次size需要加上old_size值。

并通过MMAP方式分配一个size大小的内存块。

通过set_noncontiguous函数,设置当前分配区的brk分配已经不连续了。

 /* Cannot merge with old top, so add its size back in */
    	  /* 如果是连续的brk分配(主分配区才存在) */
          if (contiguous (av))
            size = ALIGN_UP (size + old_size, pagesize); //调整size大小,分配失败之后又把oldsize给加回去了

          /* If we are relying on mmap as backup, then use larger units */
          /* brk分配失败后,使用MMAP方式作为备用方案;如果size小于1M,则使用MMAP的默认值1M */
          if ((unsigned long) (size) < (unsigned long) (MMAP_AS_MORECORE_SIZE))
            size = MMAP_AS_MORECORE_SIZE; //MMAP_AS_MORECORE_SIZ =  1024 * 1024 = 1M

          /* Don't try if size wraps around 0 */
          if ((unsigned long) (size) > (unsigned long) (nb))
            {
        	  /* 这里使用MMAP分配1M的内存块;mbrk为MMAP分配后返回的对象 */
              char *mbrk = (char *) (MMAP (0, size, PROT_READ | PROT_WRITE, 0));

              /* 如果分配成功,则处理一下 */
              if (mbrk != MAP_FAILED)
                {
                  /* We do not need, and cannot use, another sbrk call to find end */
                  brk = mbrk; //调整brk的值
                  snd_brk = brk + size; //这里设置到尾部值

                  /*
                     Record that we no longer have a contiguous sbrk region.
                     After the first time mmap is used as backup, we do not
                     ever rely on contiguous space since this could incorrectly
                     bridge regions.
                   */
                  set_noncontiguous (av);//主分配区设置不连续的标记,因为这里使用了MMAP分配
                }
            }
        }

五、主分配区分配成功后对齐裁剪操作


主分配区分配成功后,返回brk值为分配的内存地址。

后续如果是brk连续分配地址是相邻的,则直接扩容Top

如果非相邻的,则需要将老的Top chunk剩余的空间释放到bins上,并且经过一定裁剪和对齐之后,返回新的Top chunk。

1. 内存地址连续相邻直接扩容Top


​​​​​​​

      /* 分配成功的情况处理 */
      if (brk != (char *) (MORECORE_FAILURE))
        {
          if (mp_.sbrk_base == 0)
            mp_.sbrk_base = brk;
          av->system_mem += size; //调整av的系统内存大小

          /*
             If MORECORE extends previous space, we can likewise extend top size.
           */

          //判断是否是通过brk分配的用brk分配的,并且地址是连续的,则直接更新Top chunk即可(就是Top chunk的扩容)
          if (brk == old_end && snd_brk == (char *) (MORECORE_FAILURE))
            set_head (old_top, (size + old_size) | PREV_INUSE); //设置p->mchunk_size (size + PREV使用中)

2. brk方式分配地址不相邻情况


新分配的内存地址大于原来的top chunk的结束地址,说明地址是不连续的,使用MMAP分配的,则无法直接扩容Top chunk。

有连续标记,说明是brk方式分配的。这里需要将老的Top的空间以及字节对齐的一些冗余空间进行brk二次扩容,放到第一次扩容的尾部。

最终我们要返回两个值:aligned_brk(对齐后的值)和snd_brk(二次扩容的值)

这里要重点说一下:内存地址对齐的目标是让内存分配更加合理和高效,例如chunk指针地址1001,通过对齐后是1024为启始地址。如果跟老的Top不相邻,则需要将老的Top剩余的Size + 内存地址对齐产生的前置内存碎片 重新扩容到新的Top尾部。老的Top剩余的chunk则需要free。

       //新分配的内存地址大于原来的top chunk的结束地址,说明地址是不连续的,使用MMAP分配的,则无法直接扩容Top chunk
          else
            {
              front_misalign = 0;
              end_misalign = 0;
              correction = 0;
              aligned_brk = brk;

              /* handle contiguous cases 有连续标记,说明是brk方式分配的 */
              if (contiguous (av))
                {
                  /* Count foreign sbrk as system_mem.  */
                  if (old_size)
                    av->system_mem += brk - old_end; //Topchunk不连续,需要记录外部不连续内存的大小

                  /* 分配的mm内存地址需要进行内存对齐,front_misalign表示对齐的地址前有多少个可以对齐的字节*/
                  front_misalign = (INTERNAL_SIZE_T) chunk2mem (brk) & MALLOC_ALIGN_MASK;
                  if (front_misalign > 0) //如果大于0,说明可以计算这个偏移量,调整对齐后的aligned_brk地址
                    {
                      /*
                         Skip over some bytes to arrive at an aligned position.
                         We don't need to specially mark these wasted front bytes.
                         They will never be accessed anyway because
                         prev_inuse of av->top (and any chunk created from its start)
                         is always true after initialization.
                         跳过一些字节达到对齐的位置。我们不需要特别标记这些浪费的前字节。
                       */

                      correction = MALLOC_ALIGNMENT - front_misalign; //对齐可调整的字节
                      aligned_brk += correction; //aligned_brk往后调整几个字节地址,调整后的brk地址
                    }

                  /*
                     If this isn't adjacent to existing space, then we will not
                     be able to merge with old_top space, so must add to 2nd request.
                   */
                  //correction=新扩展出来的chunk需要继续扩容的大小 包含brk对齐后头部减少的字节 + 老的top的size
                  correction += old_size;

                  /* Extend the end address to hit a page boundary */
                  end_misalign = (INTERNAL_SIZE_T) (brk + size + correction); //对齐后的新的top chunk的尾部地址
                  correction += (ALIGN_UP (end_misalign, pagesize)) - end_misalign; //尾部地址需要进行一次对齐,对齐后是否需要增加字节

                  //所以:correction=brk前置的对齐字节 + 老的top的size + 新的brk尾部的对齐字节
                  //aligned_brk:为调整后的新的chunk的起始地址
                  //snd_brk:2次扩容操作的返回值

                  assert (correction >= 0);
                  snd_brk = (char *) (MORECORE (correction)); //进行2次扩容操作,snd_brk为第二次扩容操作,扩容的值:correction

                  /*
                     If can't allocate correction, try to at least find out current
                     brk.  It might be enough to proceed without failing.

                     Note that if second sbrk did NOT fail, we assume that space
                     is contiguous with first sbrk. This is a safe assumption unless
                     program is multithreaded but doesn't use locks and a foreign sbrk
                     occurred between our first and second calls.
                   */

                  if (snd_brk == (char *) (MORECORE_FAILURE))  //扩容失败
                    {
                      correction = 0;
                      snd_brk = (char *) (MORECORE (0)); //这里又变成尾部值
                    }
                  else
                    {
                      /* Call the `morecore' hook if necessary.  */
                      void (*hook) (void) = atomic_forced_read (__after_morecore_hook); //扩容成功
                      if (__builtin_expect (hook != NULL, 0))
                        (*hook)();
                    }
                }

3. mmap方式分配地址不相邻情况


这里主要调整字节对齐和偏移量。mmap方式不进行二次扩容。

最终我们要返回两个值:aligned_brk(对齐后的值)和snd_brk(不扩容则直接尾部)

 /* handle non-contiguous cases 没有连续标记的Case,使用MMAP分配的情况 */
              else
                {
                  if (MALLOC_ALIGNMENT == 2 * SIZE_SZ)
                    /* MORECORE/mmap must correctly align */
                    assert (((unsigned long) chunk2mem (brk) & MALLOC_ALIGN_MASK) == 0);
                  else
                    {
                      front_misalign = (INTERNAL_SIZE_T) chunk2mem (brk) & MALLOC_ALIGN_MASK;
                      if (front_misalign > 0)
                        {
                          /*
                             Skip over some bytes to arrive at an aligned position.
                             We don't need to specially mark these wasted front bytes.
                             They will never be accessed anyway because
                             prev_inuse of av->top (and any chunk created from its start)
                             is always true after initialization.
                           */

                          aligned_brk += MALLOC_ALIGNMENT - front_misalign; //调整对齐,aligned_brk为调整后的起始地址
                        }
                    }

                  /* Find out current end of memory */
                  if (snd_brk == (char *) (MORECORE_FAILURE))
                    {
                      snd_brk = (char *) (MORECORE (0)); //尾部
                    }
                }

4. 设置调整后的Topchunk


1. 设置av->top指向调整后的aligned_brk

2. 将老的Top chunk上剩余的空间,释放到bins上

        /* Adjust top based on results of second sbrk  前面brk也好,MMAP也好都成功分配之后的操作*/
              if (snd_brk != (char *) (MORECORE_FAILURE))
                {
                  av->top = (mchunkptr) aligned_brk; //将top指向调整过的aligned_brk地址
                  set_head (av->top, (snd_brk - aligned_brk + correction) | PREV_INUSE); //设置p->mchunk_size (size + PREV使用中)
                  av->system_mem += correction; //系统内存加上correction

                  /*
                     If not the first time through, we either have a
                     gap due to foreign sbrk or a non-contiguous region.  Insert a
                     double fencepost at old_top to prevent consolidation with space
                     we don't own. These fenceposts are artificial chunks that are
                     marked as inuse and are in any case too small to use.  We need
                     two to make sizes and alignments work out.
                   */

                  /* 合并加工后的的old_top需要回收free */
                  if (old_size != 0)
                    {
                      /*
                         Shrink old_top to insert fenceposts, keeping size a
                         multiple of MALLOC_ALIGNMENT. We know there is at least
                         enough space in old_top to do this.
                         缩小2个字节,并重新设置old_top->mchunk_size,old_size是有足够空间缩小两个字节的
                       */
                      old_size = (old_size - 4 * SIZE_SZ) & ~MALLOC_ALIGN_MASK;
                      set_head (old_top, old_size | PREV_INUSE);

                      /*
                         Note that the following assignments completely overwrite
                         old_top when old_size was previously MINSIZE.  This is
                         intentional. We need the fencepost, even if old_top otherwise gets
                         lost.
                       */
		      set_head (chunk_at_offset (old_top, old_size),
				(2 * SIZE_SZ) | PREV_INUSE); //设置
		      set_head (chunk_at_offset (old_top, old_size + 2 * SIZE_SZ),
				(2 * SIZE_SZ) | PREV_INUSE);

                      /* If possible, release the rest. */
                      if (old_size >= MINSIZE)
                        {
                          _int_free (av, old_top, 1); //释放old_top的chunk
                        }
                    }
                }

六、切割新的Topchunk分配内存


如果Top chunk的大小大于分配的值(nb + MINISIZE),则进行切割分配操作,切割出来的分配出去,剩余的remainder变成Top chunk。

MINISIZE是最小的分配内存大小,在Top上分配至少留足一个MINISIZE的chunk,因为chunk的用户区域使用中的时候会复用下一个chunk的mchunk_prev_size字段的数据空间

  /* finally, do the allocation */
  p = av->top;
  size = chunksize (p); //获取Top chunk的size :p->mchunk_size

  /* check that one of the above allocation paths succeeded */
  /* 如果Top chunk的大小大于分配的值,则进行切割分配操作,切割出来的分配出去,剩余的remainder变成Top chunk*/
  if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE))
    {
      remainder_size = size - nb;
      remainder = chunk_at_offset (p, nb);
      av->top = remainder;
      set_head (p, nb | PREV_INUSE | (av != &main_arena ? NON_MAIN_ARENA : 0));
      set_head (remainder, remainder_size | PREV_INUSE);
      check_malloced_chunk (av, p, nb);
      return chunk2mem (p);
    }

至此,我们将malloc函数的实现基本讲解完了,下一章开始讲解free函数。

本文标签: 函数源码TOPptmallocsysmalloc