glibc内存管理分析
字数 2286 2025-08-22 12:23:00

GLibc内存管理机制深入解析

1. 分配区概念

1.1 主分配区(main arena)与非主分配区(no main arena)

  • 主分配区

    • 系统中仅存在一个主分配区
    • 内存分配时需加锁确保线程安全
    • 可通过sbrkmmap两种方式向操作系统申请虚拟内存
  • 非主分配区

    • 只能通过mmap映射得到内存区域
    • 每次申请内存默认大小为"HEAP MAX SIZE"
    • 系统仅在必要时使用mmap申请内存

2. Chunk结构

2.1 使用中的chunk结构

标志位:
- P (PREV_INUSE): 0表示前一个chunk空闲,1表示使用中
- M (IS_MMAPPED): 1表示通过mmap分配,0表示不是
- A (NON_MAIN_ARENA): 1表示主分配区,0表示非主分配区

内存布局示例:

char *name = malloc(8);  // name指向Mem指针
// name-2就是Chunk指针指向的地方

2.2 空闲中的chunk结构

  • 标志位与使用中chunk相同(无M标志位)
  • fd指向下一个空闲chunk
  • bk指向前一个空闲chunk
  • fd_nextsizebk_nextsize仅出现在large bin中

2.3 chunk的空间复用

  • 为避免空间浪费,ptmalloc采用空间复用技术
  • 32位系统中最小chunk为16字节:
    • prev_size: 4字节
    • size: 4字节
    • fd: 4字节
    • bk: 4字节
  • 64位系统同理(最小chunk为32字节)

3. 空闲chunk容器

3.1 容器分类

#define NBINS 128       // 总bin数量
#define NSMALLBINS 64   // small bin数量
#define SMALLBIN_WIDTH MALLOC_ALIGNMENT
#define MIN_LARGE_SIZE (NSMALLBINS * SMALLBIN_WIDTH)

3.2 索引计算

// 判断是否在small bin范围内
#define in_smallbin_range(sz) \
  ((unsigned long)(sz) < (unsigned long)MIN_LARGE_SIZE)

// small bin索引计算
#define smallbin_index(sz) \
  (SMALLBIN_WIDTH == 16 ? (((unsigned)(sz)) >> 4) : (((unsigned)(sz)) >> 3))

// large bin索引计算(32位)
#define largebin_index_32(sz) \
  (((((unsigned long)(sz)) >> 6) <= 38) ? 56 + (((unsigned long)(sz)) >> 6) : \
   ((((unsigned long)(sz)) >> 9) <= 20) ? 91 + (((unsigned long)(sz)) >> 9) : \
   /* 其他情况省略... */)

// large bin索引计算(64位)
#define largebin_index_64(sz) \
  (((((unsigned long)(sz)) >> 6) <= 48) ? 48 + (((unsigned long)(sz)) >> 6) : \
   ((((unsigned long)(sz)) >> 9) <= 20) ? 91 + (((unsigned long)(sz)) >> 9) : \
   /* 其他情况省略... */)

// 通用bin索引计算
#define bin_index(sz) \
  ((in_smallbin_range(sz)) ? smallbin_index(sz) : largebin_index(sz))

3.3 bins的作用

  • 作为内存管理的缓存机制,减少系统调用
  • 存储不同大小的空闲内存块(chunks)
  • 共维护128个bin,使用数组存储
  • 大小相近的chunk通过双向链表链接

4. 特殊容器

4.1 fastbin

  • 用于快速分配小内存块(32位中<0x80)
  • 不改变chunk的P标志位,避免合并
  • 分配时优先查找fastbin
  • 特定时机会合并fastbin中的chunk到unsorted bin

4.2 unsorted bin

  • 释放的大于max_fast的chunk首先放入此处
  • fastbin合并后的chunk也会放入此处
  • malloc时若fastbin不满足,会先查找此处
  • 可看作bins的缓冲区,加速分配

4.3 small bin

  • 管理较小的空闲chunk(32位:16B-504B,64位:32B-1008B)
  • 每个bin维护双向环形链表
  • 链表中的chunk大小相同
  • 62个small bin(索引1-62)

4.4 large bin

  • 管理较大的空闲chunk
  • 每个bin中的chunk大小不等
  • 使用fd_nextsizebk_nextsize指针管理不同大小的chunk

5. 特殊chunk

5.1 top chunk

  • 位于arena最顶部(最高内存地址处)
  • 不属于任何bin
  • 当所有free chunk都无法满足请求时使用
  • 分配规则:
    • 若大小足够,分割为用户chunk和新top chunk
    • 否则扩展heap(主分配区用sbrk,非主分配区用mmap)

5.2 mmaped chunk

  • 当top chunk也不满足时,直接使用mmap映射
  • free时直接解除映射

5.3 last remainder

  • 当从bins中分割chunk时,剩余部分成为last remainder
  • 记录在arena的malloc_state.last_remainder

6. 内存分配与回收

6.1 分配步骤

  1. 获取分配区锁(线程安全)
  2. 检查请求大小:
    • 若<max_fast,查找fastbin
    • 若在small bin范围,查找smallbin
    • 否则处理largebin
  3. 若fastbin/smallbin无合适chunk:
    • 合并fastbin到unsortedbin
    • 检查unsortedbin
  4. 若仍无合适chunk:
    • 查找largebin
    • 尝试使用top chunk
  5. 若top chunk不足:
    • 主分配区:sbrk扩展
    • 非主分配区:mmap新heap
  6. 若请求大小>mmap阈值,直接mmap映射

6.2 回收步骤

  1. 获取分配区锁
  2. 检查指针有效性
  3. 判断是否为mmap分配:
    • 是:直接munmap
    • 否:
      • 若<max_fast且不在堆顶:放入fastbin
      • 否则:
        • 检查前一个chunk是否空闲,若空闲则合并
        • 检查是否与top chunk相邻,若相邻则合并到top
        • 否则放入unsortedbin
  4. 若合并后chunk超过FASTBIN_CONSOLIDATION_THRESHOLD:
    • 合并fastbin到unsortedbin
  5. 检查top chunk大小:
    • 若超过mmap收缩阈值:
      • 主分配区:部分返回系统(初始堆不返回)
      • 非主分配区:收缩sub-heap

7. 版本差异

  • 2.23及之前版本:无tcache机制
  • 2.26+版本:引入tcache(优先级高于fastbin,安全性低)
  • 2.29+版本:加固tcache安全性

8. 关键参数

  • max_fast:32位系统通常为0x80
  • mmap阈值:32位系统通常为128KB
  • small bin范围
    • 32位:16B-504B(8B递增)
    • 64位:32B-1008B(16B递增)

9. 系统调用

  • sbrk:主分配区扩展heap
  • mmap
    • 非主分配区分配sub-heap
    • 大内存直接分配
    • 主分配区在sbrk失败时使用

10. 安全考虑

  • 分配区锁确保线程安全
  • 空间复用减少内存浪费
  • 分箱机制提高分配效率
  • tcache在速度与安全间权衡

本文档涵盖了glibc 2.23及之前版本的内存管理核心机制,重点包括分配区、chunk结构、各类bin的管理策略,以及内存分配与回收的具体流程。对于理解ptmalloc的工作原理和进行内存相关漏洞分析具有重要意义。

GLibc内存管理机制深入解析 1. 分配区概念 1.1 主分配区(main arena)与非主分配区(no main arena) 主分配区 : 系统中仅存在一个主分配区 内存分配时需加锁确保线程安全 可通过 sbrk 和 mmap 两种方式向操作系统申请虚拟内存 非主分配区 : 只能通过 mmap 映射得到内存区域 每次申请内存默认大小为"HEAP MAX SIZE" 系统仅在必要时使用 mmap 申请内存 2. Chunk结构 2.1 使用中的chunk结构 内存布局示例: 2.2 空闲中的chunk结构 标志位与使用中chunk相同(无M标志位) fd 指向下一个空闲chunk bk 指向前一个空闲chunk fd_nextsize 和 bk_nextsize 仅出现在large bin中 2.3 chunk的空间复用 为避免空间浪费,ptmalloc采用空间复用技术 32位系统中最小chunk为16字节: prev_ size: 4字节 size: 4字节 fd: 4字节 bk: 4字节 64位系统同理(最小chunk为32字节) 3. 空闲chunk容器 3.1 容器分类 3.2 索引计算 3.3 bins的作用 作为内存管理的缓存机制,减少系统调用 存储不同大小的空闲内存块(chunks) 共维护128个bin,使用数组存储 大小相近的chunk通过双向链表链接 4. 特殊容器 4.1 fastbin 用于快速分配小内存块(32位中 <0x80) 不改变chunk的P标志位,避免合并 分配时优先查找fastbin 特定时机会合并fastbin中的chunk到unsorted bin 4.2 unsorted bin 释放的大于max_ fast的chunk首先放入此处 fastbin合并后的chunk也会放入此处 malloc时若fastbin不满足,会先查找此处 可看作bins的缓冲区,加速分配 4.3 small bin 管理较小的空闲chunk(32位:16B-504B,64位:32B-1008B) 每个bin维护双向环形链表 链表中的chunk大小相同 62个small bin(索引1-62) 4.4 large bin 管理较大的空闲chunk 每个bin中的chunk大小不等 使用 fd_nextsize 和 bk_nextsize 指针管理不同大小的chunk 5. 特殊chunk 5.1 top chunk 位于arena最顶部(最高内存地址处) 不属于任何bin 当所有free chunk都无法满足请求时使用 分配规则: 若大小足够,分割为用户chunk和新top chunk 否则扩展heap(主分配区用sbrk,非主分配区用mmap) 5.2 mmaped chunk 当top chunk也不满足时,直接使用mmap映射 free时直接解除映射 5.3 last remainder 当从bins中分割chunk时,剩余部分成为last remainder 记录在arena的 malloc_state.last_remainder 中 6. 内存分配与回收 6.1 分配步骤 获取分配区锁(线程安全) 检查请求大小: 若<max_ fast,查找fastbin 若在small bin范围,查找smallbin 否则处理largebin 若fastbin/smallbin无合适chunk: 合并fastbin到unsortedbin 检查unsortedbin 若仍无合适chunk: 查找largebin 尝试使用top chunk 若top chunk不足: 主分配区:sbrk扩展 非主分配区:mmap新heap 若请求大小>mmap阈值,直接mmap映射 6.2 回收步骤 获取分配区锁 检查指针有效性 判断是否为mmap分配: 是:直接munmap 否: 若<max_ fast且不在堆顶:放入fastbin 否则: 检查前一个chunk是否空闲,若空闲则合并 检查是否与top chunk相邻,若相邻则合并到top 否则放入unsortedbin 若合并后chunk超过FASTBIN_ CONSOLIDATION_ THRESHOLD: 合并fastbin到unsortedbin 检查top chunk大小: 若超过mmap收缩阈值: 主分配区:部分返回系统(初始堆不返回) 非主分配区:收缩sub-heap 7. 版本差异 2.23及之前版本 :无tcache机制 2.26+版本 :引入tcache(优先级高于fastbin,安全性低) 2.29+版本 :加固tcache安全性 8. 关键参数 max_ fast :32位系统通常为0x80 mmap阈值 :32位系统通常为128KB small bin范围 : 32位:16B-504B(8B递增) 64位:32B-1008B(16B递增) 9. 系统调用 sbrk :主分配区扩展heap mmap : 非主分配区分配sub-heap 大内存直接分配 主分配区在sbrk失败时使用 10. 安全考虑 分配区锁确保线程安全 空间复用减少内存浪费 分箱机制提高分配效率 tcache在速度与安全间权衡 本文档涵盖了glibc 2.23及之前版本的内存管理核心机制,重点包括分配区、chunk结构、各类bin的管理策略,以及内存分配与回收的具体流程。对于理解ptmalloc的工作原理和进行内存相关漏洞分析具有重要意义。