ptmalloc源码分析 - Top chunk的扩容函数sysmalloc实现(09)

news2024/11/23 22:31:13

目录

一、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(二次扩容的值)

       //新分配的内存地址大于原来的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的大小大于分配的值,则进行切割分配操作,切割出来的分配出去,剩余的remainder变成Top chunk。

  /* 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函数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1024665.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(25)(25.1) 光学流量传感器的测试和设置

文章目录 25.1.1 测试传感器 25.1.2 校准传感器 25.1.3 测距传感器检查 25.1.4 预解锁检查 25.1.5 首次飞行 25.1.6 第二次飞行 25.1.7 正常操作设置 25.1.8 视频示例&#xff08;Copter-3.4&#xff09; 25.1.9 空中校准 25.1.1 测试传感器 将传感器连接至自动驾驶仪…

面试官:Vue3.0里为什么要用 Proxy API 替代 defineProperty API ?

&#x1f3ac; 岸边的风&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 一、Object.defineProperty 为什么能实现响应式 小结 二、proxy 三、总结 一、Object.defineProperty 定义&am…

vue打印、vue-print-nb插件的基本使用

今天做项目碰到一个打印的需求&#xff0c;只打印一个表格&#xff0c;去网上找了些方法总结一下 打印的方法最常见的就是window.print(),这是浏览器自带的打印方法&#xff0c;方便快捷无需安装插件&#xff0c;但相应的自定义化也差无法打印页面局部&#xff0c;去网上查找资…

薄盒借周杰伦IP卖藏品 车翻在奈雪的茶

在瑞幸联名茅台、喜茶联名FENDI、茶百道联名米哈游后&#xff0c;奈雪的茶搭上了周杰伦。9月14日&#xff0c;在《范特西》专辑发行22周年之际&#xff0c;奈雪的茶推出“范特西音乐宇宙”主题的奶茶与周边。 周杰伦系IP加持&#xff0c;奈雪的茶卖爆了&#xff0c;范特西Styl…

淘宝天猫商品全网搜索接口,关键词搜索商品列表数据接口,淘宝API接口申请指南

淘宝搜索接口是一种提供更便捷的淘宝商品搜索服务的工具。通过该接口&#xff0c;用户可以更加快速地找到自己需要的商品&#xff0c;节省时间和精力。 淘宝关键字搜索接口主要用于以下几个方面的业务应用&#xff1a; 商品搜索。用户可以根据关键字搜索他们想要购买的商品。…

生信学院|09月20日《在线焊件建模——xFrame》

课程主题&#xff1a;在线焊件建模——xFrame 课程时间&#xff1a;2023年09月20日 14:00-14:30 主讲人&#xff1a;武旭 生信科技 售后服务工程师 1、3DEXPERIENCE设计平台介绍 2、xFrame设计工具使用 3、Q&A 请安装腾讯会议客户端或APP&#xff0c;微信扫描海报中的…

【深度学习】clip-interrogator clip docker 容器启动过程

文章目录 dockerfile备忘ENTRYPOINT ["bash", "/app/startProject.sh"]常用docker指令web服务脚本访问接口文件 给一张图片&#xff0c;输出图片描述。 dockerfile备忘 只有从dockerfile制作的镜像才有分层结构&#xff0c;加速传输&#xff0c;故第一步…

Linux内核源码分析 (B.2)深入理解 Linux 物理内存管理

Linux内核源码分析 (B.2)深入理解 Linux 物理内存管理 文章目录 Linux内核源码分析 (B.2)深入理解 Linux 物理内存管理[TOC] 1\. 前文回顾2\. 从 CPU 角度看物理内存模型2.1 FLATMEM 平坦内存模型2.2 DISCONTIGMEM 非连续内存模型2.3 SPARSEMEM 稀疏内存模型2.3.1 物理内存热插…

MySQL常见面试题(一)

&#x1f600;前言 在数据库管理系统中&#xff0c;存储引擎起着核心的角色&#xff0c;它决定了数据管理和存储的方式。MySQL作为一个领先的开源关系型数据库管理系统&#xff0c;提供了多种存储引擎来满足不同的需求和优化不同的应用。除了选择合适的存储引擎&#xff0c;数据…

拉格朗日乘子法思路来源

核心思路:由果索因 一. 直观理解 1. 问题描述 对于如"图1"式(等式约束优化问题, 可行域是边界), 转化成拉格朗日乘子法的思路来源: 图1: 拉格朗日乘子法问题描述图 如"图2",f为曲面.c为平面, 黑色加粗线是f和c的交线.(约束就是限制自变量的变化范围). …

Llama2-Chinese项目:2.1-Atom-7B预训练

虽然Llama2的预训练数据相对于第一代LLaMA扩大了一倍&#xff0c;但是中文预训练数据的比例依然非常少&#xff0c;仅占0.13%&#xff0c;这也导致了原始Llama2的中文能力较弱。为了能够提升模型的中文能力&#xff0c;可以采用微调和预训练两种路径&#xff0c;其中&#xff1…

聚观早报|蔚来汽车首颗自研芯片;中式汉堡正打破“麦门永存”

【聚观365】9月19日消息 蔚来汽车首颗自研芯片 中式汉堡正在打破“麦门永存” 三星Galaxy S24系列入网 特斯拉电动皮卡预订量已超过200万辆 郭明錤称iPhone 15 Pro Max需求强劲 蔚来汽车首颗自研芯片 蔚来汽车正在进行自研芯片布局&#xff0c;蔚来汽车硬件副总裁白剑上个…

慢SQL治理经验总结

在过去两年的工作中&#xff0c;我们团队曾负责大淘宝技术的慢SQL治理工作&#xff0c;作为横向的数据安全治理平台&#xff0c;如何快速准确地发现部门内所有应用的慢SQL&#xff0c;并进行高效的推动治理&#xff0c;同时覆盖多个开发、生产环境&#xff0c;是一个很大的挑战…

机器人掀起“智能热潮”:揭秘中国机器人市场的新风貌

原创 | 文 BFT机器人 中国的机器人市场和自动化形势从未像今年这样令人兴奋。去年&#xff0c;全球超过一半的工业机器人在中国销售。2023年上半年&#xff0c;中国机器人需求趋势仍在继续上升&#xff0c;根据估算&#xff0c;在此期间销售的工业机器人数量约为14.5万台&…

用Python分析文本数据的词频并词云图可视化(文末送书)

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

广播风暴的分析和解决方法(STP配置)

目录 1.广播风暴 2.解决方法&#xff1a;配置STP 1.广播风暴 以下图为例 配置交换机LSW1 <Huawei>sys Enter system view, return user view with CtrlZ. [Huawei]sysname LSW1 [LSW1]stp Sep 14 2023 05:35:15-08:00 LSW1 DS/4/DATASYNC_CFGCHANGE:OID 1.3.6.1.4.1.…

SAP中销售订单运达方导致销项税错误问题实例

近期财务同事反映SAP中一笔国内客户的销售发票会计凭证中显示码是X0&#xff0c;代出的销项税是0。 进一步检查销售发票billing&#xff0c;发现发票价格条件中销税税条件取值确实是0。 基于税码决定的系统逻辑&#xff0c;前面博客中也曾专门分析过。下面在追踪问题时就会按这…

九、蜂鸣器

九、蜂鸣器 蜂鸣器介绍蜂鸣器播放提示音 蜂鸣器介绍 音符频率对照表 蜂鸣器播放提示音 #include <REGX52.H> #include <INTRINS.H> //蜂鸣器端口&#xff1a; sbit BuzzerP1^5; void Buzzer_Delay500us() //12.000MHz {unsigned char i;_nop_();i 247;while (-…

【计算机组成原理】读书笔记第三期:内存和磁盘的关系

目录 写在开头 内存与磁盘的关系 基本关系 磁盘缓存 虚拟内存 节约内存的编程方法 通过DLL文件实现函数共有 通过调用_stdcall来降低文件程序的大小 磁盘的物理结构 结尾 写在开头 本文继续阅读总结《程序是怎样跑起来的》这本书&#xff08;作者&#xff1a;矢泽…

基于Java的流浪动物救助及领养管理设计与实现

前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f447;&#x1f3fb;…