glibc堆概述

8 篇文章 0 订阅
5 篇文章 0 订阅

glibc堆概述

1.内存管理与堆

  • 程序内存布局

在这里插入图片描述

  • brk() 和 sbrk()

    • 堆的属性是可读可写的,大小通过sbrk和brk进行控制。

    • 函数描述

      NAME
             brk, sbrk - change data segment size
      DESCRIPTION
             brk()  and  sbrk()  change the location of the program break, which de‐
             fines the end of the process's data segment (i.e., the program break is
             the  first  location  after the end of the uninitialized data segment).
             Increasing the program break has the effect of allocating memory to the
             process; decreasing the break deallocates memory.
      
             brk()  sets the end of the data segment to the value specified by addr,
             when that value is reasonable, the system has enough  memory,  and  the
             process does not exceed its maximum data size (see setrlimit(2)).
      
             sbrk() increments the program's data space by increment bytes.  Calling
             sbrk() with an increment of 0 can be used to find the current  location
             of the program break.
      
      
    • 开启ASLR,堆的起始地址在BSS段后的随即位移处。未开启ASLR,堆的起始地址sbrk会紧接着BSS段

  • mmap() 和 unmmap()

    • 当用户申请内存过大时,ptmalloc2会选择通过mmap函数创建匿名映射段供用户使用,并通过ummap函数进行回收

2. glibc的设计

  • 申请堆块,从堆中按顺序分配堆块交给用户,用户保存指向这些堆块的指针
  • 释放堆块,glibc将释放的堆块组织成链表;当两块相邻的堆块都为释放状态时将合成新的堆块,由此解决内存碎片的问题
  • 用户正在使用的堆块叫做 allocated chunk,被释放的堆叫做 free chunk,由 free chunk 组成的链表叫 bin
  • 称当前chunk低地址处相邻的chunk为 上一个(bd)chunk,高地址处相邻的chunk 为下一个(fd)chunk
  • 根据不同大小范围的chunk组成成不同的bin。 例如 fast bin small binlarge bin等等,在这些链表中的chunk叫做 fast chunksmall chunklarge chunk

3.重要的概念和结构体

  • arena

    • arena包含一片或数片连续的内存
    • 主线程的arena被称为 main arena,包含 sbrk 到 brk 之间的内存空间
    • main arena 只有堆,子线程的arena可以有数片的连续内存,被称为 heap
  • heap_info

    • 该数据结构是专门为从 Memory Mapping Segment 处申请的内存准备的,即为非主线程准备的。

    • heap_info 的主要结构如下

      #define HEAP_MIN_SIZE (32 * 1024)
      #ifndef HEAP_MAX_SIZE
      # ifdef DEFAULT_MMAP_THRESHOLD_MAX
      #  define HEAP_MAX_SIZE (2 * DEFAULT_MMAP_THRESHOLD_MAX)
      # else
      #  define HEAP_MAX_SIZE (1024 * 1024) /* must be a power of two */
      # endif
      #endif
      /* HEAP_MIN_SIZE and HEAP_MAX_SIZE limit the size of mmap()ed heaps
         that are dynamically created for multi-threaded programs.  The
         maximum size must be a power of two, for fast determination of
         which heap belongs to a chunk.  It should be much larger than the
         mmap threshold, so that requests with a size just below that
         threshold can be fulfilled without creating too many heaps.  */
      
      /***************************************************************************/
      
      /* A heap is a single contiguous memory region holding (coalesceable)
         malloc_chunks.  It is allocated with mmap() and always starts at an
         address aligned to HEAP_MAX_SIZE.  */
      
      typedef struct _heap_info
      {
        mstate ar_ptr; /* Arena for this heap. */
        struct _heap_info *prev; /* Previous heap. */
        size_t size;   /* Current size in bytes. */
        size_t mprotect_size; /* Size in bytes that has been mprotected
                                 PROT_READ|PROT_WRITE.  */
        /* Make sure the following data is properly aligned, particularly
           that sizeof (heap_info) + 2 * SIZE_SZ is a multiple of
           MALLOC_ALIGNMENT. */
        char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK];
      } heap_info;
      
  • malloc_state

    • 该结构用于管理堆,记录每个 arena 当前申请的内存的具体状态,比如说是否有空闲 chunk,有什么大小的空闲 chunk 等等。

    • 注意,main arena 的 malloc_state 并不是 heap segment 的一部分,而是一个全局变量,存储在 libc.so 的数据段。

    • 其结构如下

      struct malloc_state {
          /* Serialize access.  */
          //该变量用于控制程序串行访问同一个分配区,当一个线程获取了分配区之后,其它线程要想访问该分配区,就必须等待该线程分配完成后才能够使用。
          __libc_lock_define(, mutex);
      
          /* Flags (formerly in max_fast).  */
          //flags 记录了分配区的一些标志,比如 bit0 记录了分配区是否有 fast bin chunk ,bit1 标识分配区是否能返回连续的虚拟地址空间。
          int flags;
      
          /* Fastbins */
          //存放每个 fast chunk 链表头部的指针
          mfastbinptr fastbinsY[ NFASTBINS ];
      	
          /* Base of the topmost chunk -- not otherwise kept in a bin */
          mchunkptr top;
      
          /* The remainder from the most recent split of a small request */
          mchunkptr last_remainder;
      
          /* Normal bins packed as described above */
          mchunkptr bins[ NBINS * 2 - 2 ];
      
          /* Bitmap of bins, help to speed up the process of determinating if a given bin is definitely empty.*/
          unsigned int binmap[ BINMAPSIZE ];
      
          /* Linked list, points to the next arena */
          struct malloc_state *next;
      
          /* Linked list for free arenas.  Access to this field is serialized
             by free_list_lock in arena.c.  */
          struct malloc_state *next_free;
      
          /* Number of threads attached to this arena.  0 if the arena is on
             the free list.  Access to this field is serialized by
             free_list_lock in arena.c.  */
          INTERNAL_SIZE_T attached_threads;
      
          /* Memory allocated from the system in this arena.  */
          INTERNAL_SIZE_T system_mem;
          INTERNAL_SIZE_T max_system_mem;
      };
      
      
  • malloc_chunk

    • chunk 是 glibc 管理内存的基本单位,整个堆在初始化后,会被当成一个free chunk,称为top chun。

    • 当用户请求内存时,如果bins中没有合适的chunk,malloc会从top chunk中进行划分。如果top chunk 还不够就调用brk 扩展堆

    • 无论一个 chunk 的大小如何,处于分配状态还是释放状态,它们都使用一个统一的结构, 但是会根据分配状态有不同的呈现方式

    • 结构如下

      /*
        This struct declaration is misleading (but accurate and necessary).
        It declares a "view" into memory allowing access to necessary
        fields at known offsets from a given base. See explanation below.
      */
      struct malloc_chunk {
      
        INTERNAL_SIZE_T      prev_size;  /* Size of previous chunk (if free).  */
        INTERNAL_SIZE_T      size;       /* Size in bytes, including overhead. */
      
        struct malloc_chunk* fd;         /* double links -- used only if free. */
        struct malloc_chunk* bk;
      
        /* Only used for large blocks: pointer to next larger size.  */
        struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
        struct malloc_chunk* bk_nextsize;
      };
      

      每个字段的具体的解释如下

      • prev_size, 如果该 chunk 的**物理相邻的前一地址 chunk(两个指针的地址差值为前一 chunk 大小)**是空闲的话,那该字段记录的是前一个 chunk 的大小 (包括 chunk 头)。否则,该字段可以用来存储物理相邻的前一个 chunk 的数据。这里的前一 chunk 指的是较低地址的 chunk

      • size,该 chunk 的大小,大小必须是 2 * SIZE_SZ 的整数倍。如果申请的内存大小不是 2 * SIZE_SZ 的整数倍,会被转换满足大小的最小的 2 * SIZE_SZ 的倍数。32 位系统中,SIZE_SZ 是 4;64 位系统中,SIZE_SZ 是 8。 该字段的低三个比特位对 chunk 的大小没有影响,它们从高到低分别表示

        • NON_MAIN_ARENA,记录当前 chunk 是否不属于主线程,1 表示不属于,0 表示属于。
        • IS_MAPPED,记录当前 chunk 是否是由 mmap 分配的。
        • PREV_INUSE,记录前一个 chunk 块是否被分配。一般来说,堆中第一个被分配的内存块的 size 字段的 P 位都会被设置为 1,以便于防止访问前面的非法内存。当一个 chunk 的 size 的 P 位为 0 时,我们能通过 prev_size 字段来获取上一个 chunk 的大小以及地址。这也方便进行空闲 chunk 之间的合并。
      • fd,bk

        。 chunk 处于分配状态时,从 fd 字段开始是用户的数据。chunk 空闲时,会被添加到对应的空闲管理链表中,其字段的含义如下

        • fd 指向下一个(非物理相邻)空闲的 chunk
        • bk 指向上一个(非物理相邻)空闲的 chunk
        • 通过 fd 和 bk 可以将空闲的 chunk 块加入到空闲的 chunk 块链表进行统一管理
      • fd_nextsize, bk_nextsize

        ,也是只有 chunk 空闲的时候才使用,不过其用于较大的 chunk(large chunk)。

        • fd_nextsize 指向前一个与当前 chunk 大小不同的第一个空闲块,不包含 bin 的头指针。
        • bk_nextsize 指向后一个与当前 chunk 大小不同的第一个空闲块,不包含 bin 的头指针。
        • 一般空闲的 large chunk 在 fd 的遍历顺序中,按照由大到小的顺序排列。这样做可以避免在寻找合适 chunk 时挨个遍历。

4. 各类bins

  • fast bin

    • **因为我们把大部分时间花在了合并、分割以及中间检查的过程中。**因此,ptmalloc 中专门设计了 fast bin,对应的变量就是 malloc state 中的 fastbinsY
    • 为了更加高效地利用 fast bin,glibc 采用单向链表对其中的每个 bin 进行组织,并且每个 bin 采取 LIFO 策略,最近释放的 chunk 会更早地被分配,所以会更加适合于局部性。
    • 需要特别注意的是,fastbin 范围的 chunk 的 inuse 始终被置为 1。因此它们不会和其它被释放的 chunk 合并。
  • small bin

    • small bins 中每个 chunk 的大小与其所在的 bin 的 index 的关系为:chunk_size = 2 * SIZE_SZ *index,具体如下

      下标SIZE_SZ=4(32 位)SIZE_SZ=8(64 位)
      21632
      32448
      43264
      54080
      x24x28x
      635041008
    • small bins 中每个 bin 对应的链表采用 FIFO 的规则,所以同一个链表中先被释放的 chunk 会先被分配出去。

  • large bin

    • large bins 中一共包括 63 个 bin,每个 bin 中的 chunk 的大小不一致,而是处于一定区间范围内。此外,这 63 个 bin 被分成了 6 组,每组 bin 中的 chunk 大小之间的公差一致,具体如下:

      数量公差
      13264B
      216512B
      384096B
      4432768B
      52262144B
      61不限制
  • unsorted bin

    • 一定大小的chunk被释放时,在进入large bin 或者 small bin 之前,会先加入unsorted bin
    • 主要有两个来源
      • 当一个较大的 chunk 被分割成两半后,如果剩下的部分大于 MINSIZE,就会被放到 unsorted bin 中。
      • 释放一个不属于 fast bin 的 chunk,并且该 chunk 不和 top chunk 紧邻时,该 chunk 会被首先放到 unsorted bin 中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值