堆内存(4)——向操作系统申请内存sysmalloc

向操作系统申请内存sysmalloc

sysmalloc流程图

sysmalloc

尝试mmap分配try_mmap

如果空间用完

static void * sysmalloc(INTERNAL_SIZE_T nb, mstate av) {
   
    //声明结构体
    .....
  if (av == NULL
      || ((unsigned long) (nb) >= (unsigned long) (mp_.mmap_threshold)
          && (mp_.n_mmaps < mp_.n_mmaps_max)))
    {
      char *mm;           /* return value from mmap call*/

    try_mmap:
      if (MALLOC_ALIGNMENT == 2 * SIZE_SZ)
        size = ALIGN_UP (nb + SIZE_SZ, pagesize);
      else
        size = ALIGN_UP (nb + SIZE_SZ + MALLOC_ALIGN_MASK, pagesize);
      tried_mmap = true;
      if ((unsigned long) (size) > (unsigned long) (nb))
        {
          mm = (char *) (MMAP (0, size, PROT_READ | PROT_WRITE, 0));
          if (mm != MAP_FAILED)
            {
              if (MALLOC_ALIGNMENT == 2 * SIZE_SZ)
                  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);
                  set_prev_size (p, correction);
                  set_head (p, (size - correction) | IS_MMAPPED);
                }
              else
                {
                  p = (mchunkptr) mm;
                  set_prev_size (p, 0);
                  set_head (p, size | IS_MMAPPED);
                }
              if (front_misalign > 0)
                {
                  correction = MALLOC_ALIGNMENT - front_misalign;
                  p = (mchunkptr) (mm + correction);
                  set_prev_size (p, correction);
                  set_head (p, (size - correction) | IS_MMAPPED);
                }
              else
                {
                  p = (mchunkptr) mm;
                  set_prev_size (p, 0);
                  set_head (p, size | IS_MMAPPED);
                }

              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);
            }
        }
  if (av == NULL)
    return 0;

sysmalloc的代码很长,sysmalloc用来扩展进程的堆,在sysmalloc中判断了是通过mmap分配还是通过sbrk分配内存,首先,如果满足以下条件之一,则直接使用mmap分配内存

  1. top chunk未初始化,系统还有可以用来mmap分配的内存
  2. 需要分配的内存大于使用mmap分配的阀值mp_.mmap_threshold

值得注意的是mmap分配内存需要用request2size重新计算需要分配内存的大小,这是由于使用mmap直接分配内存不需要添加到管理free bin的链表中,因此不存在前后的关系,当一个chunk被使用时,无法借用后一个chunk的prev_size字段,所以需要将prev_size中的SIZE_SZ加上,并进行内存对齐操作。

接下来判断需要分配的内存大小是否会溢出,然后就调用MMAP分配内存,MMAP是一个宏定义,最后就是通过系统调用来分配内存,这个放在后面和sbrk一起看。

再往下就是通过set_head 在chunk中的size参数设置标志位,chunk使用8字节对齐,在size中留下了3个未使用的标志位,分别用来标记PREV_INUSE §、IS_MMAPPED (M)、NON_MAIN_ARENA (N)

#define set_head(p, s)       ((p)->mchunk_size = (s))

随后更新全局变量_mp,将mp_.n_mmaps加1,表示通过mmap分配的chunk格式,mp_.max_n_mmaps最大chunk个数。p_.mmapped_mem标识已经通过mmap分配的内存大小,mp_.max_mmapped_mem对应可分配内存的最大值

最后,通过chunk2mem返回chunk中内存的起始指针。

#define chunk2mem(p)   ((void*)((char*)(p) + 2*SIZE_SZ))

如果mmap失败则返回0;

分配前准备

保留arena的头尾指针和大小并初始化非连续块snd_brk为假

  old_top = av->top;
  old_size = chunksize (old_top);
  old_end = (char *) (chunk_at_offset (old_top, old_size));

  brk = snd_brk = (char *) (MORECORE_FAILURE);

针对非主分配区

针对非主分配区因为每个heap都是按照HEAP_MAX_SIZE对齐的,且都是通过MMAP分配的,可以通过heap_for_ptr快速取出top chunk信息

//对于非主分配区,因为每个heap是按照HEAP_MAX_SIZE的大小分配且对齐的,因此通过heap_for_ptr就能快速。取出heap_info指针 
  if (av != &main_arena)
    {
      heap_info *old_heap, *heap;
      size_t old_heap_size;

      /* First try to extend the current heap. */
      //通过heap_for_ptr获取源old_top的指针
      old_heap = heap_for_ptr (old_top);
      old_heap_size = old_heap->size;
      //增长的内存段无保护(即未增长其他已分配内存区域),尝试向heap的高地址处增加heap当前使用的大小,即top chunk的大小
      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;
          set_head (old_top, (((char *) old_heap + old_heap->size) - (char *) old_top)| PREV_INUSE);
        }

首先通过heap_for_ptr就能快速。取出heap_info指针,获得heap_info指针将旧堆已使用大小保存在old_heap_size中,如果当前top chunk的大小比申请内存不够,首先会尝试扩展top chunk则调用grow_heap尝试扩展topchunk,随后通过set head修改top chunk的size为新值(存在对齐操作)。

尝试扩展top chunk

如果满足if说明top chunk空间不足了,因此首先通过grow_heap尝试向heap的高地址处增加heap当前使用的大小,即top chunk的大小,如果grow_heap成功,top chunk大小变成MINSIZE + nb

static int
grow_heap (heap_info *h, long diff)
{
  size_t pagesize = GLRO (dl_pagesize);
  long new_size;

  diff = ALIGN_UP (diff, pagesize);
  new_size = (long) h->size + diff;
  if ((unsigned long) new_size > (unsigned long) HEAP_MAX_SIZE)
    return -1;

  if ((unsigned long) new_size > h->mprotect_size)
    {
      if (__mprotect ((char *) h + h->mprotect_size,
                      (unsigned long) new_size - h->mprotect_size,
                      PROT_READ | PROT_WRITE) != 0)
        return -2;

      h->mprotect_size = new_size;
    }

  h->size = new_size;
  LIBC_PROBE (memory_heap_more, 2, h, h->size);
  return 0;
}

这段代码其实最关键的是h->size = new_size这一样,表示重新设置heap的大小至new_size

创建新堆

如果无法扩展top chunk,回到sysmalloc 尝试分配新堆

      //如果堆增长失败,则创建新堆mp_.top_pad参数表示多分配的内存
      else if ((heap = new_heap (nb + (MINSIZE + sizeof (*heap)), mp_.top_pad)))
        {
        //新堆分配成功则设置arena的区域和大小等,所有的prev构成单向链表
          /* Use a newly allocated heap.  */
          heap->ar_ptr = av;
          heap->prev = old_heap;
          av->system_mem += heap->size;
          /* Set up the new top.  */
          top (av) = chunk_at_offset (heap, sizeof (*heap));
          set_head (top (av), (heap->size - sizeof (*heap)) | PREV_INUSE);

          /* 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);
          //分割old_size,2 * SIZE_SZ和2 * SIZE_SZ
          if (old_size >= MINSIZE)
            {
              set_head (chunk_at_offset (old_top, old_size), (2 * SIZE_SZ) | PREV_INUSE);
              set_foot (chunk_at_offset (old_top, old_size), (2 * SIZE_SZ));
              set_head (old_top, old_size | PREV_INUSE | NON_MAIN_ARENA);
          if (old_size >= MINSIZE)
            {
              set_head (chunk_at_offset (old_top, old_size), (2 * SIZE_SZ) | PREV_INUSE);
              set_foot (chunk_at_offset (old_top, old_size), (2 * SIZE_SZ));
              set_head (old_top, old_size | PREV_INUSE | NON_MAIN_ARENA);
              //TODO:如果之前分配了2*HEAP_MAX_SIZE old_top>HEAP_MAX_SIZE是否会归还内存给操作系统
              _int_free (av, old_top, 1);
            }
          //内存较小则分割为2 * SIZE_SZ和old_size + 2 * SIZE_SZ
          else
            {
              set_head (old_top, (old_size + 2 * SIZE_SZ) | PREV_INUSE);
              set_foot (old_top, (old_size + 2 * SIZE_SZ));
            }
        }

grow_heap失败,大部分情况都是应为heap的使用大小已经接近最大值HEAP_MAX_SIZE这时候会尝试用new_heap重新分配一个heap,top_pad表示在内存分配的时候多分配了内存。

new heap

new heap流程图如下

image-20220217164222806

new_heap会尝试MMAP三次,具体过程如下

static heap_info *
new_heap (size_t size, size_t top_pad)
{
  size_t pagesize = GLRO (dl_pagesize);
  char *p1, *p2;
  unsigned long ul;
  heap_info *h;

  if (size + top_pad < HEAP_MIN_SIZE)
    size = HEAP_MIN_SIZE;
  else if (size + top_pad <= HEAP_MAX_SIZE)
    size += top_pad;
  else if (size > HEAP_MAX_SIZE)
    return 0;
  else
    size = HEAP_MAX_SIZE;
  size = ALIGN_UP (size, pagesize);

  /* A memory region aligned to a multiple of HEAP_MAX_SIZE is needed.
     No swap space needs to be reserved for the following large
     mapping (on Linux, this is the case for all non-writable mappings
     anyway). */
  p2 = MAP_FAILED;
  //开始尝试分配新堆aligned_heap_area表示上一次MMAP分配后结束的地址,如果存在尝试从该地址分配HEAP_MAX_SIZE的地址
  if (aligned_heap_area)
    {
      p2 = (char *) MMAP (aligned_heap_area, HEAP_MAX_SIZE, PROT_NONE,
                          MAP_NORESERVE);
      aligned_heap_area = NULL;
      if (p2 != MAP_FAILED && ((unsigned long) p2 & (HEAP_MAX_SIZE - 1)))
        {
          __munmap (p2, HEAP_MAX_SIZE);
          p2 = MAP_FAILED;
        }
    }

此时需要对size进行调整,如果需要的内存(加上多分配的内存)小于堆的最小值,则需要申请内存的大小为HEAP_MIN_SIZE如果超过了HEAP_MAX_SIZE则放弃创建新堆(如果没有try_mmap则会尝试try_mmap TODO:为什么不把设置size放到try_mmap前,这样如果大小合适就直接mmap了),其他情况SIZE则为HEAP_MAX_SIZE(HEAP_MIN_SIZE<SIZE<=top)。

然后将内存页对齐

aligned_heap_area表示上一次MMAP分配后结束的地址,首先new_heap会尝试在上一次分配的尾部开始MMAP,分配成功后会检查是否对齐,不对齐的话会取消映射,并将分配结果设为失败。并进行第二次分配。

  if (p2 == MAP_FAILED)
    {
      p1 = (char *) MMAP (0, HEAP_MAX_SIZE << 1, PROT_NONE, MAP_NORESERVE);
      if (p1 != MAP_FAILED)
        {
          p2 = (char *) (((unsigned long) p1 + (HEAP_MAX_SIZE - 1))
                         & ~(HEAP_MAX_SIZE - 1));
          ul = p2 - p1;
          if (ul)
            __munmap (p1, ul);
          else
            aligned_heap_area = p2 + HEAP_MAX_SIZE;
          __munmap (p2 + HEAP_MAX_SIZE, HEAP_MAX_SIZE - ul);
        }

第一次失败后会尝试分配2*HEAP_MAX_SIZE的内存,地址由内核决定,并把基地址保存到p1,如果成功从中截取与HEAP_MAX_SIZE对齐的内存,同样的分配失败需要释放这块内存。

如果失败了进行第三次分配

      else
        {
          /* Try to take the chance that an allocation of only HEAP_MAX_SIZE
             is already aligned. */
          p2 = (char *) MMAP (0, HEAP_MAX_SIZE, PROT_NONE, MAP_NORESERVE);
          if (p2 == MAP_FAILED)
            return 0;

          if ((unsigned long) p2 & (HEAP_MAX_SIZE - 1))
            {
              __munmap (p2, HEAP_MAX_SIZE);
              return 0;
            }
        }

第三次分配HEAP_MAX_SIZE大小的内存,地址由内核决定,不对齐的话也算分配失败,并返回0。

  if (__mprotect (p2, size, PROT_READ | PROT_WRITE) != 0)
    {
      __munmap (p2, HEAP_MAX_SIZE);
      return 0;
    }
  h = (heap_info *) p2;
  h->size = size;
  h->mprotect_size = size;
  LIBC_PROBE (memory_heap_new, 2, h, h->size);
  return h

如果有一次分配内存成功,首先会检查该内存是否可写,不可写也会算失败,随后将在p2处构建heap_info结构体,保存分配的的内存和已用内存,此外提供了新建堆的探针。

继续回到sysmalloc中非主分配区的分配中

else if ((heap = new_heap (nb + (MINSIZE + sizeof (*heap)), mp_.top_pad)))
        {
        //新堆分配成功则设置arena的区域和大小等,所有的prev构成单向链表
          /* Use a newly allocated heap.  */
          heap->ar_ptr = av;
          heap->prev = old_heap;
          av->system_mem += heap->size;
          /* Set up the new top.  */
          top (av) = chunk_at_offset (heap, sizeof (*heap));
          set_head (top (av), (heap->size - sizeof (*heap)) | PREV_INUSE);

          /* 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);
          //分割old_size,2 * SIZE_SZ和2 * SIZE_SZ
          if (old_size >= MINSIZE)
            {
              set_head (chunk_at_offset (old_top, old_size), (2 * SIZE_SZ) | PREV_INUSE);
              set_foot (chunk_at_offset (old_top, old_size), (2 * SIZE_SZ));
              set_head (old_top, old_size | PREV_INUSE | NON_MAIN_ARENA);
              //TODO:如果之前分配了2*HEAP_MAX_SIZE old_top>HEAP_MAX_SIZE是否会归还内存给操作系统
              _int_free (av, old_top, 1);
            }
          //内存较小则分割为2 * SIZE_SZ和old_size + 2 * SIZE_SZ
          else
            {
              set_head (old_top, (old_size + 2 * SIZE_SZ) | PREV_INUSE);
              set_foot (old_top, (old_size + 2 * SIZE_SZ));
            }
        }

在新堆创建成功后做了以下几件事:

  1. 打上该分配区的标记
  2. 将堆加到prev单链表中
  3. 修改系统内存大小
  4. 设置新堆的top chunk size

此时新堆包含heap_info top chunk 、以及大于size的未使用部分

然后需要创建两个2*SIZE_SZ的块分割两个堆,将低一个2*SIZE_SZ块放在旧堆的末尾并打上已用的标记(标记第一个堆尾的位置)并在第二个堆后、第二个则用来将第一个块的大小保存到prev单链表中。(保存prev_sizeprev可以复用)随后通过_int_free释放

如果内存不够则将原来的top chunk分割成old_size+2*SIZE_SZ2*SIZE_SZ

      else if (!tried_mmap)
        /* We can at least try to use to mmap memory.  */
        goto try_mmap;
......
    return 0;

如果增长堆和新堆增长失败,且未尝试mmap则通过mmap申请内存,mmap失败则返回0。

主分配区的处理

  else     /* av == main_arena */


    { /* Request enough space for nb + pad + overhead */
      size = nb + mp_.top_pad + MINSIZE;
      /*
         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.
       */
      //如果竞技场连续则申请的内存减去原有内存
      if (contiguous (av))

对于主分配区需要分配的内存大小为申请的内存加上需要多分配的,再加上最小大小。分配时针对主分配去是否连续有两种不同的处理逻辑,contiguous宏用来判断连续标志位是否置位。

#define contiguous(M)          (((M)->flags & NONCONTIGUOUS_BIT) == 0)
        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);

      /*
         Don't try to call MORECORE if argument is so big as to appear
         negative. Note that since mmap takes size_t arg, it may succeed
         below even if we cannot call MORECORE.
       */
      //通过系统调用分配内存,如果新地址小于原本的brk,会do_munmap释放虚拟内存,反之使用do_brk增加堆
      if (size > 0)
        {
          brk = (char *) (MORECORE (size));
          LIBC_PROBE (memory_sbrk_more, 2, brk, size);
        }
      //内存分配成功可以以原子操作进入hook函数
      if (brk != (char *) (MORECORE_FAILURE))
        {
          /* Call the `morecore' hook if necessary.  */
          void (*hook) (void) = atomic_forced_read (__after_morecore_hook);
          if (__builtin_expect (hook != NULL, 0))
            (*hook)();

如果连续则需要分配的大小可以减去原来的大小,并对齐内存。然后通过系统调用(sbrk)分配内存,如果新地址小于原本的brk,会do_munmap释放虚拟内存,反之使用do_brk增加堆,并提供一个探针。

分配成功提供一个HOOK点。分配失败则进入else部分

      else
        {
          /*
             If have mmap, try using it as a backup when MORECORE fails or
             cannot be used. This is worth doing on systems that have "holes" in
             address space, so sbrk cannot extend to give contiguous space, but
             space is available elsewhere.  Note that we ignore mmap max count
             and threshold limits, since the space will not be used as a
             segregated mmap region.
           */

          /* Cannot merge with old top, so add its size back in */
          if (contiguous (av))
            size = ALIGN_UP (size + old_size, pagesize);

          /* If we are relying on mmap as backup, then use larger units */
          if ((unsigned long) (size) < (unsigned long) (MMAP_AS_MORECORE_SIZE))
            size = MMAP_AS_MORECORE_SIZE;
         /* Don't try if size wraps around 0 */
          if ((unsigned long) (size) > (unsigned long) (nb))
            {
              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;
                  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);
                }
            }
        }

把内存还给旧top,如果小于mmap的最小大小则分配MMAP_AS_MORECORE_SIZE的内存,由于不能扩展主分配区的top chunk基本上是因为接近边界,因此使用MMAP分配时位置由内核决定。brk保存头snd_brk保存尾指针。设置arena不连续标志。

          if (brk == old_end && snd_brk == (char *) (MORECORE_FAILURE))
            set_head (old_top, (size + old_size) | PREV_INUSE);
         //分配的top chunk变大了但是brk变小了说明内存chunk单链表损坏oops
          else if (contiguous (av) && old_size && brk < old_end)
            /* Oops!  Someone else killed our space..  Can't touch anything.  */
            malloc_printerr ("break adjusted to free malloc space");
else

首先判断是否是通过brk分配的用brk分配的只需要更新top chunk的大小就行

如果是连续的释放了空间这说明chunk单链表损坏oops

存在外部分配
          else
            {
              front_misalign = 0;
              end_misalign = 0;
              correction = 0;
              aligned_brk = brk;

              /* handle contiguous cases */
              if (contiguous (av))
                {
                  /* Count foreign sbrk as system_mem.  */
                  //非第一次分配,计算非sbrk分配的连续内存,将其他线程的累加
                  if (old_size)
                    av->system_mem += brk - old_end;

                  /* Guarantee alignment of first new chunk made from this space */
                 //第二块空间的第一个块对齐
                  front_misalign = (INTERNAL_SIZE_T) chunk2mem (brk) & MALLOC_ALIGN_MASK;                 //处理因为对齐产生的不连续的问题,放弃原始top chunk后面一部分的内存大小,并将这
                  //这一部分内存大小补到新内存后,首先结算堆内存补上内存后的结束地址保存在correction
                  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.
                       */

                      correction = MALLOC_ALIGNMENT - front_misalign;
                      aligned_brk += correction;
                    }

                  /*
                     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 += old_size;
                  //调用MORECORE再分配一次将新分配内存地址保存在snd_brk中
                  /* Extend the end address to hit a page boundary */
                  end_misalign = (INTERNAL_SIZE_T) (brk + size + correction);
                  correction += (ALIGN_UP (end_misalign, pagesize)) - end_misalign;

                  assert (correction >= 0);
                  snd_brk = (char *) (MORECORE (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.
                   */

进入第三个判断表示新分配的内存地址大于原来的top chunk的结束地址,但是不连续。这种情况下,如果分配区的连续标志位置位,则表示不是通过MMAP分配的,肯定有其他线程调用了brk在堆上分配了内存。av->system_mem += brk - old_end;表示将其他线程分配的内存记入到主分配区的内存大小,随后调用MORECORE继续分配一次。

 if (snd_brk == (char *) (MORECORE_FAILURE))
                    {
                      correction = 0;
                      snd_brk = (char *) (MORECORE (0));
                    }
                  //成功了则提供一个HOOK点
                  else
                    {
                      /* Call the `morecore' hook if necessary.  */
                      void (*hook) (void) = atomic_forced_read (__after_morecore_hook);
                      if (__builtin_expect (hook != NULL, 0))
                        (*hook)();
                    }

如果分配失败会放弃对齐需要的内存

外部分配不连续
              else
                {
                  if (MALLOC_ALIGNMENT == 2 * SIZE_SZ)
                    /* MORECORE/mmap must correctly align */
                    assert (((unsigned long) chunk2mem (brk) & MALLOC_ALIGN_MASK) == 0);
                  //将原来剩余的大小保存到front_misalign
                  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;
                        }
                    }
                  //分配失败则找到原来sbrk的结尾
                  /* Find out current end of memory */
                  if (snd_brk == (char *) (MORECORE_FAILURE))
                    {
                      snd_brk = (char *) (MORECORE (0));
                    }
                }

不连续需要放弃原来的top chunk后面一部分的内存,并在新内存处补上这块内存,对齐后的新内存大小保存在aligned_brk中。

分配失败则找到原来sbrk的结尾

              if (snd_brk != (char *) (MORECORE_FAILURE))
                {
                  av->top = (mchunkptr) aligned_brk;
                  set_head (av->top, (snd_brk - aligned_brk + correction) | PREV_INUSE);
                  av->system_mem += correction;
                  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.
                       */
                      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);
                        }
                    }

分配成功则将top chunk设置为对齐后的brk,并将剩余的大小补充到top chunk后面,如果原来的块还有未使用的空间则创建两个2*SIZE_SZ用来维护prev单链表

更新top chunk

  if ((unsigned long) av->system_mem > (unsigned long) (av->max_system_mem))
    av->max_system_mem = av->system_mem;
  check_malloc_state (av);

  /* finally, do the allocation */
  p = av->top;
  size = chunksize (p);

  /* check that one of the above allocation paths succeeded */
  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);
    }

  /* catch all failure paths */
  __set_errno (ENOMEM);
  return 0;
}

sysmalloc最后一部分代码就是将top chunk返回,首先检查系统分配的内存和可用内存,并将对应分配区的top chunk指针,然后从top chunk中分配用户需要的chunk并返回。

sysmalloc总结

进入sysmalloc则表示 top chunk的空间不足了,对于非主分配区就通过grow_heap增加top chunk ,如果失败则通过new_heap重新分配一个heap,并将该分配区的top chunk指向新heap的空闲内存。

对于主分配区,尝试用brk增长堆以增加top chunk如果失败通过MMAP分配。

最后分配成功则更新top chunk所需要的内存和mp_结构体

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值