ptmalloc源码分析

阅读: 评论:0

ptmalloc源码分析

ptmalloc源码分析

目录

一、heap_info是什么?

二、heap_info结构体含义和实现

三、new_heap创建一个heap_info

四、grow_heap对heap进行size扩容

五、shrink_heap对heap进行size缩容

六、delete_heap删除一个heap


一、heap_info是什么?


上一章节《ptmalloc源码分析 - 多线程争抢竞技场Arena的实现(04)》我们讲解了多线程环境下,面对线程之间的竞争,ptmalloc除了主分配区外,还会去创建非主分配区。因为线程在调用malloc的时候,首先会去获取一个分配区,如果当前的分配区都被锁定并且没有新的分配区可用的时候,ptmalloc就会去创建一个新的非主分配区

我们也从上一章节,知道了非主分配区的几个特性:

  • 每个进程有一个主分配区,也可以允许有多个非主分配区。
  • 主分配区可以使用brk和mmap来分配,而非主分配区只能使用mmap来映射内存块
  • 非主分配区的数量一旦增加,则不会减少。
  • 主分配区和非主分配区形成一个环形链表进行管理。通过malloc_state->next来链接

在函数_int_new_arena中主要是创建一个新的分配区,该分配区主要是非主分配区类型。主分配区在ptmalloc_init中初始化,并且设置了全局变量main_arena的值。而_int_new_arena中主要调用的是new_heap来创建和初始化一个非主分配区。

这里主要包含几个部分:

  • _heap_info:非主分配区的结构体

  • new_heap(size_t size, size_t top_pad):创建一个新的非主分配区

  • grow_heap(heap_info *h, long diff):扩大一个分配区的size

  • shrink_heap(heap_info *h, long diff):缩小一个分配区的size

  • delete_heap(heap):删除一个分配区结构

二、heap_info结构体含义和实现


 _heap_info来存储分配区的基础信息:

  • ar_ptr:指向当前heap所属的竞技场Arena

  • prev:链表,指向前一个堆的heap_info结构

  • size:当前分配区大小

  • mprotect_size:记录了堆中多大的空间是可读写的

  • pad:通过一个数组的方式,包含该heap_info结构体所需要的内存地址。并且,能够按照0x10字节对齐(x86中则是8字节对齐)

typedef struct _heap_info {mstate ar_ptr; /* 指向当前heap所属的竞技场Arena Arena for this heap. */struct _heap_info *prev; /* 链表,指向前一个堆的heap Previous heap. */size_t size; /* 当前分配区大小 Current size in bytes. */size_t mprotect_size; /* 记录了堆中多大的空间是可读写的 Size in bytes that has been mprotectedPROT_READ|PROT_WRITE.  *//*  Make sure the following data is properly aligned, particularlythat sizeof (heap_info) + 2 * SIZE_SZ is a multiple ofMALLOC_ALIGNMENT. *//* 用以堆其该结构体,使其能够按照0x10字节对齐(x86中则是8字节对齐) */char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK];
} heap_info;

三、new_heap创建一个heap_info


new_heap函数说明:创建一个新的堆空间 ,从 mmap 区域映射一块内存页来作为 heap

映射内存页的大小:

  • 在 32 位系统上,该函数每次映射 1M 内存,映射的内存块地址按 1M 对齐;

  • 在 64 为系统上,该函数映射 64M 内存,映射的内存块地址按 64M 对齐。

/* HEAP_MIN_SIZE 和 HEAP_MAX_SIZE 最小和最大值*/
/* HEAP_MAX_SIZE:根据操作系统取值,32位 1M;64位 64M*/
#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/* DEFAULT_MMAP_THRESHOLD_MAX : 定义不同操作系统下的值大小 */
# if __WORDSIZE == 32
#  define DEFAULT_MMAP_THRESHOLD_MAX (512 * 1024) //512K
# else
#  define DEFAULT_MMAP_THRESHOLD_MAX (4 * 1024 * 1024 * sizeof(long)) //32M
# endif
#endif

new_heap函数实现逻辑:

  1. 首先,判断默认设置的size大小,如果size需要在HEAP_MIN_SIZE和HEAP_MAX_SIZE之间(64位系统,HEAP_MIN_SIZE=32k,HEAP_MAX_SIZE=64M)。
  2. 如果size设置过大,则报错;如果过小,则默认使用HEAP_MIN_SIZE作为size的最小值
  3. 创建内存映射区域的时候,使用MMAP函数。每次分配以HEAP_MAX_SIZE为每一页的大小(64位系统 分配一次64M)
  4. 默认情况下,aligned_heap_area值为NULL,所以代码会直接进入p1的分配流程。p1的分配流程中,会一次性分配HEAP_MAX_SIZE*2两个页,如果分配成功,则将aligned_heap_area设置成第2页的起始位置
  5. 当第二次进来分配的时候,aligned_heap_area值不为空,说明前面已经分配过一次,并且aligned_heap_area保存了前一次分配第二页的起始地址,所以这次分配直接使用前一次分配的第二页。如果此次分配失败,则继续跳转到p1重新分配。
  6. 如果p1分配两个页失败,则尝试分配1个页(大小HEAP_MAX_SIZE),如果还是失败则报错
/* 创建一个新的堆空间 从 mmap 区域映射一块内存页来作为 heap* 分配的页大小:* 在 32 位系统上,该函数每次映射 1M 内存,映射的内存块地址按 1M 对齐;* 在 64 为系统上,该函数映射 64M 内存,映射的内存块地址按 64M 对齐。*/
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;/* size 在 HEAP_MIN_SIZE 和 HEAP_MAX_SIZE之间,最小:HEAP_MIN_SIZE,超过HEAP_MAX_SIZE 则返回0 */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;elsesize = 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 largemapping (on Linux, this is the case for all non-writable mappingsanyway). *//* aligned_heap_area: 是上一次调用 mmap 分配内存的结束虚拟地址,并已经按照 HEAP_MAX_SIZE 大小对齐。* 如果aligned_heap_area不为空,则从上次虚拟内存地址开始映射HEAP_MAX_SIZE大小的地址 */p2 = MAP_FAILED;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;}}/* 如果第一次分配(aligned_heap_area=NULL) 或者 P2分配失败(aligned_heap_area不为NULL),则开始重新分配* p1表示第一次分配,调用MMAP分配2倍HEAP_MAX_SIZE的内存映射块,本次使用内存块的第一块部分,并将aligned_heap_area指向第二块部分* */if (p2 == MAP_FAILED) {p1 = (char *) MMAP(0, HEAP_MAX_SIZE << 1, PROT_NONE, MAP_NORESERVE); //分配一块两倍HEAP_MAX_SIZE大小的内存映射块,可以分两次使用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);elsealigned_heap_area = p2 + HEAP_MAX_SIZE; //aligned_heap_area 记录下一次分配的地址值__munmap(p2 + HEAP_MAX_SIZE, HEAP_MAX_SIZE - ul);} else {/* 如果P1分配2倍的HEAP_MAX_SIZE失败,则再次重试分配HEAP_MAX_SIZE,分配失败则报错*//* Try to take the chance that an allocation of only HEAP_MAX_SIZEis 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;}}}if (__mprotect(p2, size, PROT_READ | PROT_WRITE) != 0) {__munmap(p2, HEAP_MAX_SIZE);return 0;}h = (heap_info *) p2; //写入heap_info结构h->size = size; //设置大小h->mprotect_size = size; //设置大小LIBC_PROBE(memory_heap_new, 2, h, h->size);return h;
}

四、grow_heap对heap进行size扩容


 grow_heap:对堆进行扩容(但是size需要小于分配页的大小HEAP_MAX_SIZE);
实际就是调整size(扩大size,但是size需要小于分配页大小)

/*** 对堆进行扩容,但是size需要小于分配页的大小HEAP_MAX_SIZE* 实际就是调整size(扩大size,但是size需要小于分配页大小)*/
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;
}

五、shrink_heap对heap进行size缩容


 shrink_heap:缩小堆区:实际就是缩小size,通过MMAP函数(参数:MAP_FIXED)

static int shrink_heap(heap_info *h, long diff) {long new_size;new_size = (long) h->size - diff;if (new_size < (long) sizeof(*h))return -1;/* Try to re-map the extra heap space freshly to save memory, and make itinaccessible.  See malloc-sysdep.h to know when this is true.  */if (__glibc_unlikely(check_may_shrink_heap())) {if ((char *) MMAP((char *) h + new_size, diff, PROT_NONE, MAP_FIXED)  //丢弃操作== (char *) MAP_FAILED)return -2;h->mprotect_size = new_size;} else__madvise((char *) h + new_size, diff, MADV_DONTNEED);/*fprintf(stderr, "shrink %p %08lxn", h, new_size);*/h->size = new_size;LIBC_PROBE(memory_heap_less, 2, h, h->size);return 0;
}

六、delete_heap删除一个heap


/* Delete a heap. */#define delete_heap(heap) do {									      if ((char *) (heap) + HEAP_MAX_SIZE == aligned_heap_area)		      aligned_heap_area = NULL;					      __munmap ((char *) (heap), HEAP_MAX_SIZE);			      } while (0)

本文发布于:2024-02-04 18:14:25,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170713425658174.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:源码   ptmalloc
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23