分箱式内存管理

  对于空闲的 chunk,ptmalloc 采用分箱式内存管理方式,根据空闲 chunk 的大小和处于的状态将其放在四个不同的 bin 中,这四个空闲 chunk 的容器包括 fast bins,unsorted bin,small bins 和 large bins。Fast bins 是小内存块的高速缓存,当一些大小小于 64 字节的 chunk 被回收时,首先会放入 fast bins 中,在分配小内存时,首先会查看 fast bins 中是否有合适的内存块,如果存在,则直接返回 fast bins 中的内存块,以加快分配速度。Usorted bin 只有一个,回收的 chunk 块必须先放到 unsorted bin 中,分配内存时会查看 unsorted bin 中是否有合适的 chunk,如果找到满足条件的 chunk,则直接返回给用户,否则将 unsorted bin 的所有 chunk 放入 small bins 或是 large bins 中。Small bins 用于存放固定大小的 chunk,共 64 个 bin,最小的 chunk 大小为 16 字节或 32 字节,每个 bin 的大小相差 8 字节或是 16 字节,当分配小内存块时,采用精确匹配的方式从 small bins 中查找合适的 chunk。Large bins 用于存储大于等于 512B 或 1024B 的空闲 chunk,这些 chunk 使用双向链表的形式按大小顺序排序,分配内存时按最近匹配方式从 large bins 中分配 chunk。

Small bins

  ptmalloc使用 small bins 管理空闲小 chunk,每个 small bin 中的 chunk 的大小与 bin 的 index 有如下关系:

Chunk_size=2 * SIZE_SZ * index

  • ptmalloc 维护了 62 个双向环形链表(即 small bins 有 62 个 bin)

Large bins

  比 small bins 大的bin用large bins管理。

  • large bins 63 个双向链表(即 large bins 有 63 个 bin)
  • 每个 bin 中的 chunk 大小不是一个固定公差的等差数列,而是分成 6 组 bin,每组 bin 是一个固定公差的等差数列,每组的 bin 数量依次为 32、16、8、4、2、1,公差依次为 64B、512B、4096B、32768B、262144B 等。
  • 可以将 small bins 和 large bins 存放在同一个包含 128 个 chunk 的数组上,我们可以根据数组下标计算出该 bin 的 chunk 大小(small bins)或是 chunk 大小范围(large bins),也可以根据需要分配内存块大小计算出所需 chunk所属 bin 的 index,ptmalloc 使用了一组宏巧妙的实现了这种计算。
  1. #define NBINS 128
  2. #define NSMALLBINS 64
  3. #define SMALLBIN_WIDTH MALLOC_ALIGNMENT
  4. #define MIN_LARGE_SIZE (NSMALLBINS * SMALLBIN_WIDTH)
  5. #define in_smallbin_range(sz) \
  6. ((unsigned long)(sz) < (unsigned long)MIN_LARGE_SIZE)
  7. #define smallbin_index(sz) \
  8. (SMALLBIN_WIDTH == 16 ? (((unsigned)(sz)) >> 4) : (((unsigned)(sz)) >> 3))
  9. #define largebin_index_32(sz) \
  10. (((((unsigned long)(sz)) >> 6) <= 38)? 56 + (((unsigned long)(sz)) >> 6): \
  11. ((((unsigned long)(sz)) >> 9) <= 20)? 91 + (((unsigned long)(sz)) >> 9): \
  12. ((((unsigned long)(sz)) >> 12) <= 10)? 110 + (((unsigned long)(sz)) >> 12): \
  13. ((((unsigned long)(sz)) >> 15) <= 4)? 119 + (((unsigned long)(sz)) >> 15): \
  14. ((((unsigned long)(sz)) >> 18) <= 2)? 124 + (((unsigned long)(sz)) >> 18): \
  15. 126)
  16. // XXX It remains to be seen whether it is good to keep the widths of
  17. // XXX the buckets the same or whether it should be scaled by a factor
  18. // XXX of two as well.
  19. #define largebin_index_64(sz) \
  20. (((((unsigned long)(sz)) >> 6) <= 48)? 48 + (((unsigned long)(sz)) >> 6): \
  21. 36
  22. ((((unsigned long)(sz)) >> 9) <= 20)? 91 + (((unsigned long)(sz)) >> 9): \
  23. ((((unsigned long)(sz)) >> 12) <= 10)? 110 + (((unsigned long)(sz)) >> 12): \
  24. ((((unsigned long)(sz)) >> 15) <= 4)? 119 + (((unsigned long)(sz)) >> 15): \
  25. ((((unsigned long)(sz)) >> 18) <= 2)? 124 + (((unsigned long)(sz)) >> 18): \
  26. 126)
  27. #define largebin_index(sz) \
  28. (SIZE_SZ == 8 ? largebin_index_64 (sz) : largebin_index_32 (sz))
  29. #define bin_index(sz) \
  30. ((in_smallbin_range(sz)) ? smallbin_index(sz) : largebin_index(sz))
  • in_smallbin_range(sz):sz 大小属于 small bins 置 1,否则置 0。
  • smallbin_index(sz):将 sz 大小的 chunk 转换成对应的数组下标。(对 samll bin)
  • largebin_index_32(sz) :将 sz 大小的 chunk 转换成对应的数组下标。(对 32 位 large bin)
  • largebin_index_64(sz):将 sz 大小的 chunk 转换成对应的数组下标。(对 64 位 large bin)
  • define largebin_index(sz):将 sz 大小的 chunk 转换成对应的数组下标。(根据 SIZE_SZ 判断 large bin 转换宏)
  • bin_index(sz) :将 sz 大小的 chunk 转换成对应的数组下标。(判断属于 large bin 还是 small bin)
  • 对于 SIZE_SZ 为 4B 的平台,bin[0]和 bin[1]是不存在的,因为最小的 chunk 为 16B,small bins 一共 62 个,large bins 一共 63 个,加起来一共 125 个 bin。而 NBINS 定义为 128,其实 bin[0] 和 bin[127] 都不存在,bin[1] 为 unsorted bin 的 chunk 链表头。
  1. typedef struct malloc_chunk* mbinptr;
  2. /* addressing -- note that bin_at(0) does not exist */
  3. #define bin_at(m, i) \
  4. (mbinptr) (((char *) &((m)->bins[((i) - 1) * 2])) \
  5. - offsetof (struct malloc_chunk, fd))
  6. /* analog of ++bin */
  7. #define next_bin(b) ((mbinptr)((char*)(b) + (sizeof(mchunkptr)<<1)))
  8. /* Reminders about list directionality within bins */
  9. #define first(b) ((b)->fd)
  10. #define last(b) ((b)->bk)
  11. /* Take a chunk off a bin list */
  12. #define unlink(P, BK, FD) { \
  13. FD = P->fd; \
  14. BK = P->bk; \
  15. if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \
  16. malloc_printerr (check_action, "corrupted double-linked list", P); \
  17. else { \
  18. FD->bk = BK; \
  19. 40
  20. BK->fd = FD; \
  21. if (!in_smallbin_range (P->size) \
  22. && __builtin_expect (P->fd_nextsize != NULL, 0)) { \
  23. assert (P->fd_nextsize->bk_nextsize == P); \
  24. assert (P->bk_nextsize->fd_nextsize == P); \
  25. if (FD->fd_nextsize == NULL) { \
  26. if (P->fd_nextsize == P) \
  27. FD->fd_nextsize = FD->bk_nextsize = FD; \
  28. else { \
  29. FD->fd_nextsize = P->fd_nextsize; \
  30. FD->bk_nextsize = P->bk_nextsize; \
  31. P->fd_nextsize->bk_nextsize = FD; \
  32. P->bk_nextsize->fd_nextsize = FD; \
  33. } \
  34. } else { \
  35. P->fd_nextsize->bk_nextsize = P->bk_nextsize; \
  36. P->bk_nextsize->fd_nextsize = P->fd_nextsize; \
  37. } \
  38. } \
  39. } \
  40. }
  • bin_at(m, i):通过 bin index 获得 bin 的链表头。
  • next_bin(b):获得下一个 bin 的地址,根据前面的分析,我们知道只需要将当前 bin 的地址向后移动两个指针的长度就得到下一个 bin 的链表头地址。
  • first(b):获得 bin 的第一个可用 chunk
  • last(b):获得 bin 的最后一个可用的 chunk
  • unlink(P, BK, FD):将 chunk 从所在的空闲链表中取出来,注意 large bins 中的空闲 chunk 可能处于两个双向循环链表中,unlink 时需要从两个链表中都删除。
    • __builtin_expect:这个指令是gcc引入的,作用是允许程序员将最有可能执行的分支告诉编译器。

内容来源

《Glibc内存管理》

《Glibc内存管理》笔记DAY4的更多相关文章

  1. 《Glibc内存管理》笔记DAY5

    目录 分箱式内存管理 Unsorted bin Fast bins 核心结构体分析 malloc_state 内容来源 分箱式内存管理 Unsorted bin   Unsorted bin 可以看作 ...

  2. 《Glibc内存管理》笔记DAY3

    目录 边界标记法 内容来源 边界标记法 /* conversion from malloc headers to user pointers, and back */ #define chunk2me ...

  3. 《Glibc内存管理》笔记DAY2

    目录 Ptmalloc内存管理设计 Main_arena 与 non_main_arena chunk 的组织 空闲 chunk 容器 sbrk 与 mmap 内存分配概述 内存回收概述 边界标记法 ...

  4. 《Glibc内存管理》笔记DAY1

    目录 x86_64栈和mmap固定映射地址 内存的延迟分配 内核数据结构 mm_struct Heap 操作相关函数 Mmap 映射区域操作相关函数 内容来源 x86_64栈和mmap固定映射地址   ...

  5. 2万字|30张图带你领略glibc内存管理精髓(因为OOM导致了上千万损失)

    前言 大家好,我是雨乐. 5年前,在上家公司的时候,因为进程OOM造成了上千万的损失,当时用了一个月的时间来分析glibc源码,最终将问题彻底解决. 最近在逛知乎的时候,发现不少人有对malloc/f ...

  6. 读书摘要观后感与总结:《Glibc内存管理:ptmalloc2源代码分析》

    更新中 在Linux平台下做漏洞利用的时候,针对于Heap部分总是有些不求甚解,下面开个博文来记录下<Glibc内存管理:ptmalloc2源代码分析>这本书的读后感和收获,一些简单的点将 ...

  7. glibc内存管理那些事儿

    本文转载自glibc内存管理那些事儿 Linux内存空间简介 32位Linux平台下进程虚拟地址空间分布如下图: 进程虚拟地址空间分布 图中,0xC0000000开始的最高1G空间是内核地址空间,剩下 ...

  8. 黑马程序员_ Objective-c 内存管理笔记

    引用计数器 当一个对象被创建出来,就要分配给内存这个对象,当不用这个对象的时候,就要及时的回收,为了可以明确知道对象有没有被使用,就要用引用计数器来体现,只要计数器不为0,表明对象被使用中. 1.方法 ...

  9. Java内存管理笔记

    java内存管理机制 在java中,内存管理由JVM完全负责,java中的"垃圾回收器"负责自动回收无用对象占据的内存资源,这样可以大大减少程序猿在内存管理上花费的时间,可以更集中 ...

随机推荐

  1. js入门之字符串常用的方法

    一. 概念理解基本包装类型 1. 基本包装类型 三种基本包装类型 String var s = new String('123dddd'); Number Boolean 简单类型没有方法和属性 之所 ...

  2. php中的特殊标签

    参考:https://www.freebuf.com/column/212586.html 今天看到这篇文章讲到了ctf中的一些关于php标签的小姿势,我虽然不打ctf,但是平常做php的代码审计也经 ...

  3. python运行出现TypeError: super() takes at least 1 argument (0 given)错误

    在写继承子类的时候出现了TypeError: super() takes at least 1 argument (0 given)的error: 源代码(python3中完美可运行): class ...

  4. 前台.cshtml得到cookie值方法

    function Cookie_() { $.ajax({ url: "/Login_/do_cookie",//请求地址 dataType: "json",/ ...

  5. DataTable通过Select进行过滤

    DataTable方法测试 //测试DataTable的select DataTable dt = new DataTable(); //a.OrderType, //a.[Status] dt.Co ...

  6. redis 与 序列化

    概念 序列化:把对象转化为可传输的字节序列过程称为序列化. 反序列化:把字节序列还原为对象的过程称为反序列化. 为什么需要序列化 序列化最终的目的是为了对象可以跨平台存储,和进行网络传输.而我们进行跨 ...

  7. java疑问

    1. new String("abc")究竟创建几个对象? 答: 一个或两个, 如果常量池中原来有"abc", 那么只创建一个对象; 如果常量池中原来没有&qu ...

  8. vue2 路由,运动

  9. vue2 自定义键盘事件

  10. Makefile:248: /usr/local/otp_src_18.1/make/x86_64-unknown-linux-gnu/otp_ded.mk: No such file

    安装erlang的时候,使用make命令一直报这个错 Makefile:248: /usr/local/otp_src_18.1/make/x86_64-unknown-linux-gnu/otp_d ...