`
mqzhuang
  • 浏览: 187567 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

Glibc内存管理--ptmalloc2源代码分析(二十八)

阅读更多

5.7.2.3 分配large bin chunk(二)

 如果通过上面的方式从最合适的 small bin large bin 中都没有分配到需要的 chunk ,则查看比当前 bin index 大的 small bin large bin 是否有空闲 chunk 可利用来分配所需的 chunk 。源代码实现如下

/*
      Search for a chunk by scanning bins, starting with next largest
      bin. This search is strictly by best-fit; i.e., the smallest
      (with ties going to approximately the least recently used) chunk
      that fits is selected.

      The bitmap avoids needing to check that most blocks are nonempty.
      The particular case of skipping all bins during warm-up phases
      when no chunks have been returned yet is faster than it might look.
    */
    ++idx;
    bin = bin_at(av,idx);
    block = idx2block(idx);
    map = av->binmap[block];
    bit = idx2bit(idx);

  获取下一个相邻 bin 的空闲 chunk 链表,并获取该 bin 对于 binmap 中的 bit 位的值。 Binmap 中的标识了相应的 bin 中是否有空闲 chunk 存在。 Binmap block 管理,每个 block 为一个 int ,共 32 bit ,可以表示 32 bin 中是否有空闲 chunk 存在。使用 binmap 可以加快查找 bin 是否包含空闲 chunk 。这里只查询比所需 chunk 大的 bin 中是否有空闲 chunk 可用。

    for (;;) {
      /* Skip rest of block if there are no more set bits in this block.  */
      if (bit > map || bit == 0) {
        do {
          if (++block >= BINMAPSIZE)  /* out of bins */
            goto use_top;
        } while ( (map = av->binmap[block]) == 0);

        bin = bin_at(av, (block << BINMAPSHIFT));
        bit = 1;
      }

 Idx2bit() 宏将 idx 指定的位设置为 1 ,其它位清零, map 表示一个 block unsigned int )值,如果 bit 大于 map ,意味着 map 0 ,该 block 所对应的所有 bins 中都没有空闲 chunk ,于是遍历 binmap 的下一个 block ,直到找到一个不为 0 block 或者遍历完所有的 block 。退出循环遍历后,设置 bin 指向 block 的第一个 bit 对应的 bin ,并将 bit 置为 1 ,表示该 block bit 1 对应的 bin ,这个 bin 中如果有空闲 chunk ,该 chunk 的大小一定满足要求。

      /* Advance to bin with set bit. There must be one. */
      while ((bit & map) == 0) {
        bin = next_bin(bin);
        bit <<= 1;
        assert(bit != 0);
      }
在一个block遍历对应的bin,直到找到一个bit不为0退出遍历,则该bit对于的bin中有空闲chunk存在。

      /* Inspect the bin. It is likely to be non-empty */
      victim = last(bin);
将bin链表中的最后一个chunk赋值为victim。

      /*  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;
如果victim与bin链表头指针相同,表示该bin中没有空闲chunk,binmap中的相应位设置不准确,将binmap的相应bit位清零,获取当前bin下一个bin,将bit移到下一个bit位,即乘以2。

      }
      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(victim, bck, fwd);
当前bin中的最后一个chunk满足要求,获取该chunk的大小,计算切分出所需chunk后剩余部分的大小,然后将victim从bin的链表中取出。

        /* Exhaust */
        if (remainder_size < MINSIZE) {
          set_inuse_bit_at_offset(victim, size);
          if (av != &main_arena)
            victim->size |= NON_MAIN_ARENA;

 如果剩余部分的大小小于 MINSIZE ,将整个 chunk 分配给应用层,设置 victim 的状态为 inuse ,如果当前分配区为非主分配区,设置 victim 的非主分配区标志位。

        }
        /* 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 (__builtin_expect (fwd->bk != bck, 0))
            {
              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;
            }

 从victim 中切分出所需的 chunk ,剩余部分作为一个新的 chunk 加入到 unsorted bin 中。如果剩余部分 chunk 属于 small bins ,将分配区的 last remainder chunk 设置为剩余部分构成的 chunk ;如果剩余部分 chunk 属于 large bins ,将剩余部分 chunk chunk size 链表指针设置为 NULL ,因为 unsorted bin 中的 chunk 是不排序的,这两个指针无用,必须清零。

          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);
设置victim和remainder的状态,由于remainder为空闲chunk,所以需要设置该chunk的foot。

        }
        check_malloced_chunk(av, victim, nb);
        void *p = chunk2mem(victim);
        if (__builtin_expect (perturb_byte, 0))
          alloc_perturb (p, bytes);
        return p;
	调用chunk2mem()获得chunk中可用的内存指针,返回给应用层,退出。
      }
    }

 如果从所有的 bins 中都没有获得所需的 chunk ,可能的情况为 bins 中没有空闲 chunk ,或者所需的 chunk 大小很大,下一步将尝试从 top chunk 中分配所需 chunk 。源代码实现如下:

  use_top:
    /*
      If large enough, split off the chunk bordering the end of memory
      (held in av->top). Note that this is in accord with the best-fit
      search rule.  In effect, av->top is treated as larger (and thus
      less well fitting) than any other available chunk since it can
      be extended to be as large as necessary (up to system
      limitations).

      We require that av->top always exists (i.e., has size >=
      MINSIZE) after initialization, so if it would otherwise be
      exhausted by current request, it is replenished. (The main
      reason for ensuring it exists is that we may need MINSIZE space
      to put in fenceposts in sysmalloc.)
    */
    victim = av->top;
    size = chunksize(victim);
将当前分配区的top chunk赋值给victim,并获得victim的大小。

    if ((unsigned long)(size) >= (unsigned long)(nb + MINSIZE)) {
      remainder_size = size - nb;
      remainder = chunk_at_offset(victim, nb);
      av->top = remainder;
      set_head(victim, nb | PREV_INUSE |
               (av != &main_arena ? NON_MAIN_ARENA : 0));
      set_head(remainder, remainder_size | PREV_INUSE);

      check_malloced_chunk(av, victim, nb);
      void *p = chunk2mem(victim);
      if (__builtin_expect (perturb_byte, 0))
        alloc_perturb (p, bytes);
      return p;
    }

 由于top chunk 切分出所需 chunk 后,还需要 MINSIZE 的空间来作为 fencepost ,所需必须满足 top chunk 的大小大于所需 chunk 的大小加上 MINSIZE 这个条件,才能从 top chunk 中分配所需 chunk 。从 top chunk 切分出所需 chunk 的处理过程跟前面的 chunk 切分类似,不同的是,原 top chunk 切分后的剩余部分将作为新的 top chunk ,原 top chunk fencepost 仍然作为新的 top chunk fencepost ,所以切分之后剩余的 chunk 不用 set_foot

#ifdef ATOMIC_FASTBINS
    /* When we are using atomic ops to free fast chunks we can get
       here for all block sizes.  */
    else if (have_fastchunks(av)) {
      malloc_consolidate(av);
      /* restore original bin index */
      if (in_smallbin_range(nb))
        idx = smallbin_index(nb);
      else
        idx = largebin_index(nb);
}

 如果top chunk 也不能满足要求,查看 fast bins 中是否有空闲 chunk 存在,由于开启了 ATOMIC_FASTBINS 优化情况下, free 属于 fast bins chunk 时不需要获得分配区的锁,所以在调用 _int_malloc() 函数时,有可能有其它线程已经向 fast bins 中加入了新的空闲 chunk ,也有可能是所需的 chunk 属于 small bins ,但通过前面的步骤都没有分配到所需的 chunk ,由于分配 small bin chunk 时在前面的步骤都不会调用 malloc_consolidate() 函数将 fast bins 中的 chunk 合并加入到 unsorted bin 中。所在这里如果 fast bin 中有 chunk 存在,调用 malloc_consolidate() 函数,并重新设置当前 bin index 。并转到最外层的循环,尝试重新分配 small bin chunk 或是 large bin chunk 。如果开启了 ATOMIC_FASTBINS 优化,有可能在由其它线程加入到 fast bins 中的 chunk 被合并后加入 unsorted bin 中,从 unsorted bin 中就可以分配出所需的 large bin chunk 了,所以对没有成功分配的 large bin chunk 也需要重试。

#else
    /*
      If there is space available in fastbins, consolidate and retry,
      to possibly avoid expanding memory. This can occur only if nb is
      in smallbin range so we didn't consolidate upon entry.
    */
    else if (have_fastchunks(av)) {
      assert(in_smallbin_range(nb));
      malloc_consolidate(av);
      idx = smallbin_index(nb); /* restore original bin index */
}

 如果top chunk 也不能满足要求,查看 fast bins 中是否有空闲 chunk 存在,如果 fast bins 中有空闲 chunk 存在,在没有开启 ATOMIC_FASTBINS 优化的情况下,只有一种可能,那就是所需的 chunk 属于 small bins ,但通过前面的步骤都没有分配到所需的 small bin chunk ,由于分配 small bin chunk 时在前面的步骤都不会调用 malloc_consolidate() 函数将 fast bins 中的空闲 chunk 合并加入到 unsorted bin 中。所在这里如果 fast bins 中有空闲 chunk 存在,调用 malloc_consolidate() 函数,并重新设置当前 bin index 。并转到最外层的循环,尝试重新分配 small bin chunk

#endif
    /*
       Otherwise, relay to handle system-dependent cases
    */
    else {
      void *p = sYSMALLOc(nb, av);
      if (p != NULL && __builtin_expect (perturb_byte, 0))
        alloc_perturb (p, bytes);
      return p;
山穷水尽了,只能想系统申请内存了。sYSMALLOc()函数可能分配的chunk包括small bin chunk,large bin chunk和large chunk。将在下一节中介绍该函数的实现。
    }
  }

 至此 _int_malloc() 函数的代码就罗列完了,当还有两个关键函数没有分析,一个为 malloc_consolidate() ,另一个为 sYSMALLOc() ,将在下面的章节介绍其实现。

 

 

 

 

 

0
0
分享到:
评论

相关推荐

    Glibc内存管理--ptmalloc2源代码分析(三十四)

    《Glibc内存管理--ptmalloc2源代码分析》 Glibc是GNU项目提供的C语言标准库,它在Linux系统中扮演着至关重要的角色。其中,内存管理是Glibc中的核心部分,它负责程序运行时的内存分配与释放,对系统的性能有着深远...

    glibc内存管理ptmalloc源代码分析PDF

    在分析glibc内存管理的ptmalloc源代码之前,我们需要先了解一些基础知识,包括操作系统对内存的分配和管理方法,以及glibc内存分配机制。 内存管理是操作系统的一个核心功能,它负责维护和管理计算机系统中的物理和...

    glibc内存管理ptmalloc源代码分析-高清PDF-pdf版

    《glibc内存管理ptmalloc源代码分析》是一份深入探讨Linux系统中glibc库内存管理机制的专业资料。glibc,全称GNU C Library,是Linux操作系统下广泛使用的C语言标准库,其中ptmalloc是glibc中负责动态内存分配的核心...

    glibc内存管理ptmalloc源代码分析.pdf

    glibc内存管理ptmalloc源代码分析

    glibc内存管理ptmalloc源代码分析@华庭1

    在深入探讨Glibc内存管理的Ptmalloc源代码之前,我们先来了解一下内存管理的基本概念。内存管理是操作系统和编程语言库中的核心组件,它负责有效地分配和回收内存,以确保程序的高效运行和资源的有效利用。 2.1 X86...

    Glibc内存管理Ptmalloc2源代码分析

    标题与描述中的关键词“Glibc内存管理Ptmalloc2源代码分析”指向了对Glibc中Ptmalloc2这一内存管理器的深入探讨。本文将围绕这一主题,详细解析Ptmalloc2的核心概念、设计原则、数据结构以及关键功能,同时结合给定...

    glibc内存管理ptmalloc源代码分析4.pdf

    标题与描述概述的知识点主要集中在glibc内存管理的细节,特别是ptmalloc的源代码分析,这对于深入理解C库如何高效地处理内存分配和回收至关重要。以下是对这些知识点的详细解析: ### Glibc内存管理与ptmalloc ###...

    glibc内存管理ptmalloc源代码分析1

    在深入探讨Glibc内存管理的Ptmalloc源代码之前,我们先来了解一下内存管理的基本概念和Glibc中的Ptmalloc2。内存管理是操作系统和应用程序中的核心部分,它负责为程序分配和释放内存,以确保资源的有效利用和避免...

    glibc内存管理ptmalloc源代码分析

    ### glibc内存管理ptmalloc源代码分析 #### 1. 问题 在开发一款NoSQL系统的过程中,我们遇到一个令人头疼的问题:系统中使用的内存管理模块,在高并发、高负载的场景下,虽然已将内存释放给C运行时库(即glibc),...

    glibc内存管理ptmalloc源代码分析-清晰版.pdf

    ### glibc内存管理ptmalloc源代码分析-清晰版 #### 一、背景介绍与文档概览 本文档针对glibc中的ptmalloc2内存管理模块进行了深入的源代码分析,旨在帮助开发者更好地理解ptmalloc的工作原理及其内部机制。文档...

    Python-用于检测glibc堆ptmalloc的gdbpython库

    `glibc`是GNU项目提供的C标准库,而`ptmalloc`是它的一部分,负责程序的内存分配与管理。`ptmalloc`优化了内存分配效率,但在某些情况下也可能成为安全漏洞的来源。 `GDB`(GNU调试器)是一款强大的调试工具,它...

    内存管理与调试详细剖析

    本文将深入探讨内存管理的基本原理及其调试方法,特别关注于Linux环境下的Glibc内存管理库以及Ptmalloc2的具体实现。 #### 二、基础知识 ##### 2.1 X86平台Linux进程内存布局 **2.1.1 32位模式下进程内存经典布局...

    malloc源码分析glibc库

    通过对glibc库中的ptmalloc源代码进行深入分析,我们不仅了解了其内部实现机制,还能够针对实际应用中的内存管理问题提出有效的解决方案。这对于提升程序性能、减少内存碎片以及提高系统的整体稳定性具有重要意义。...

    pwn学习总结(八)—— 堆(持续更新)

    学习自《glibc内存管理ptmalloc源代码分析》庄明强 著 部分资料参考自互联网 chunk 描述: 当用户通过malloc等函数申请空间时,实际上是从堆中分配内存 目前 Linux 标准发行版中使用的是 glibc 中的堆分配器:...

    堆漏洞的利用技巧1

    《堆漏洞的利用技巧》 堆溢出是计算机安全领域中的一个重要...同时,阅读glibc内存管理的源代码分析对于提高理解和实践能力非常有帮助。通过这些知识的学习,不仅可以增强安全意识,也能提升在CTF竞赛中的攻防能力。

Global site tag (gtag.js) - Google Analytics