秦皇岛北京网站建设,网站建设网站优化,备案网站用户名是什么,万网如何上传网站目录
一、sysmalloc函数基本分配逻辑
二、强制try_mmap分配方式
三、非主分配区分配的实现
1. 设置老的Top chunk的参数
2. 尝试使用grow_heap函数
3. 尝试使用new_heap函数
4. 尝试使用try_mmap方式
四、主分配区分配的实现
1. 设置Top扩容的size值
2. brk分配成功的…目录
一、sysmalloc函数基本分配逻辑
二、强制try_mmap分配方式
三、非主分配区分配的实现
1. 设置老的Top chunk的参数
2. 尝试使用grow_heap函数
3. 尝试使用new_heap函数
4. 尝试使用try_mmap方式
四、主分配区分配的实现
1. 设置Top扩容的size值
2. brk分配成功的方式
3. brk分配失败采用MMAP分配
五、主分配区分配成功后对齐裁剪操作
1. 内存地址连续相邻直接扩容Top
2. brk方式分配地址不相邻情况
3. mmap方式分配地址不相邻情况
4. 设置调整后的Topchunk
六、切割新的Topchunk分配内存 前面三章我们讲解了malloc的核心分配函数_int_malloc。_int_malloc核心是从我们管理的bins上去寻找空闲的chunk并分配内存。如果没有空闲的内存则到Top chunk上去分配。如果Top chunk也无法满足分配的场景则需要调用sysmalloc进行内存的分配。
本章节我们重点讲sysmalloc的具体实现。
一、sysmalloc函数基本分配逻辑 sysmalloc定义当Top chunk内存空间不足的时候就会调用sysmalloc函数进行内存分配操作。
参数入参nb为需要分配的av为分配区状态机对象
sysmalloc基本逻辑
非主分配区通过MMAP并生成heap对象进行Top chunk的扩容操作。非主分配区使用MMAP分配32位操作系统每次分配1M64位系统每次分配64M非主分配区如果分配失败则调用try_mmap直接通过MMAP返回需要的内存主分配区里面一般情况下调用brk对Top chunk进行扩容主分配区如果通过brk分配失败则才调用MMAP方式分配主分配区brk分配每次除了需要分配的nb内存字节外额外还会扩容mp_.top_pad128K的内存空间如果主分配区使用MMAP分配每次分配1M无论主分配区还是非主分配区分配成功后都要对分配的内存进行一系列对齐、标记、裁剪、释放等操作最后切割当前需要分配的内存并将Remainder chunk指向Top chunk地址
/*sysmalloc handles malloc cases requiring more memory from the system.On entry, it is assumed that av-top does not have enoughspace to service request for nb bytes, thus requiring that av-topbe extended or replaced.调用系统分配函数sysmalloc说明进入sysmalloc则表示 top chunk的空间不足了需要进行扩容av-topnb请求的内存大小mstate内存分配状态机(分配区)*/static void *
sysmalloc (INTERNAL_SIZE_T nb, mstate av)
{mchunkptr old_top; /* 老的Topchunk的地址指针 mstate-top incoming value of av-top */INTERNAL_SIZE_T old_size; /* 老的Top chunk的大小 its size */char *old_end; /* 老的Top chunk的尾部地址 its end address */long size; /* 第一次分配的内存size arg to first MORECORE or mmap call */char *brk; /* 通过brk分配后返回的对象 return value from MORECORE */long correction; /* 用于记录第二次分配的值 arg to 2nd MORECORE call */char *snd_brk; /* 第二次处理后返回的值也是第一次的尾部 2nd return val */INTERNAL_SIZE_T front_misalign; /* 不可用的新空间的头部字节 unusable bytes at front of new space */INTERNAL_SIZE_T end_misalign; /* 新内存块尾部对齐的字节 partial page left at end of new space */char *aligned_brk; /* 对齐后的brk值 aligned offset into brk */mchunkptr p; /* 返回的结果p the allocated/returned chunk */mchunkptr remainder; /* Top chunk切割后剩余的remainder chunk remainder from allocation */unsigned long remainder_size; /* Top chunk切割后剩余的remainder chunk 的size its size */size_t pagesize GLRO (dl_pagesize);bool tried_mmap false;
二、强制try_mmap分配方式 try_mmap是尝试直接进行mmap的方式分配一段内存。当非主分配区通过扩容heap或者new heap都失败的情况下采用try_mmap方式分配。
但是需要符合MMAP的阀值以及系统支持MMAPnb分配的内存为大对象超过128k。该场景则直接返回MMAP分配的内存chunk不调整Top chunk。
如果MMAP方式分配成功对分配的内存进行对齐操作需要计算偏移量并标记该内存是IS_MMAPPED类型的。最后直接返回内存地址。 /*If have mmap, and the request size meets the mmap threshold, andthe system supports mmap, and there are few enough currentlyallocated mmapped regions, try to directly map this requestrather than expanding top.1. avNULL则直接采用MMAP的方式分配内存2. nb分配的内存为大对象超过128k并且符合MMAP的阀值以及系统支持MMAP则采用MMAP分配mp_.n_mmaps_max65536mp_.mmap_threshold128*1024需要goto try_mmap才会进入MMAP分配逻辑该场景则直接返回MMAP分配的内存chunk不调整Top chunk*/if (av NULL|| ((unsigned long) (nb) (unsigned long) (mp_.mmap_threshold) (mp_.n_mmaps mp_.n_mmaps_max))){char *mm; /* MMAP的返回值 return value from mmap call*//* 尝试进行MMAP分配 */try_mmap:/*Round up size to nearest page. For mmapped chunks, the overheadis one SIZE_SZ unit larger than for normal chunks, because thereis no following chunk whose prev_size field could be used.See the front_misalign handling below, for glibc there is noneed for further alignments unless we have have high alignment.*//* 进行size的内存对齐操作 */if (MALLOC_ALIGNMENT 2 * SIZE_SZ)size ALIGN_UP (nb SIZE_SZ, pagesize); //调整sizeelsesize ALIGN_UP (nb SIZE_SZ MALLOC_ALIGN_MASK, pagesize);//调整sizetried_mmap true;/* Dont try if size wraps around 0 */if ((unsigned long) (size) (unsigned long) (nb)){/* 直接调用MMAP映射一块内存大小为size可读可写*/mm (char *) (MMAP (0, size, PROT_READ | PROT_WRITE, 0)); //调用MMAP的方式获取一块size大小的内存/* 如果分配成功 */if (mm ! MAP_FAILED){/*The offset to the start of the mmapped region is storedin the prev_size field of the chunk. This allows us to adjustreturned start address to meet alignment requirements hereand in memalign(), and still be able to compute properaddress argument for later munmap in free() and realloc().*//* 分配的mm内存地址需要进行内存对齐front_misalign表示对齐的地址前有多少个可以对齐的字节*/if (MALLOC_ALIGNMENT 2 * SIZE_SZ){/* For glibc, chunk2mem increases the address by 2*SIZE_SZ andMALLOC_ALIGN_MASK is 2*SIZE_SZ-1. Each mmaped area is pagealigned and therefore definitely MALLOC_ALIGN_MASK-aligned. */assert (((INTERNAL_SIZE_T) chunk2mem (mm) MALLOC_ALIGN_MASK) 0);front_misalign 0;}elsefront_misalign (INTERNAL_SIZE_T) chunk2mem (mm) MALLOC_ALIGN_MASK;/* 如果有可以对齐的字节 */if (front_misalign 0){correction MALLOC_ALIGNMENT - front_misalign; //计算偏移量p (mchunkptr) (mm correction); //mm偏移量后得到最终返回的chunk的地址//说白了对齐后将便宜部分的字节当成了一个新的chunk结构free的时候要一起清理set_prev_size (p, correction); //设置前一个p-mchunk_prev_sizeset_head (p, (size - correction) | IS_MMAPPED); //设置p-mchunk_size并且需要减去偏移量 size MMAP映射}else{/* 这里不需要对齐操作*/p (mchunkptr) mm; //获取得到chunk的指针地址set_prev_size (p, 0); //这里没有偏移所以设置前一个mchunk_prev_size为0set_head (p, size | IS_MMAPPED); //设置p-mchunk_size size MMAP映射}/* update statistics 更新各种状态*/int new atomic_exchange_and_add (mp_.n_mmaps, 1) 1;atomic_max (mp_.max_n_mmaps, new);unsigned long sum;sum atomic_exchange_and_add (mp_.mmapped_mem, size) size;atomic_max (mp_.max_mmapped_mem, sum);check_chunk (av, p);return chunk2mem (p); //这里返回内存地址非chunk地址}}}
三、非主分配区分配的实现 如果是非主分配区则通过heap_info方式获取堆信息结构并且通过MMAP分配内存。
1. 设置老的Top chunk的参数
brk和snd_brk默认值都设置为失败。brk为第一次分配后的指针地址snd_brk为第二次分配的地址同时也是第一次分配内存的尾部地址。 /* There are no usable arenas and mmap also failed. *//* 如果分配区为空则不能分配返回 */if (av NULL)return 0;/* Record incoming configuration of top *//* 获取top 的 顶部信息*/old_top av-top; //老的Topchunk的地址old_size chunksize (old_top); //老的Topchunk的sizeold_end (char *) (chunk_at_offset (old_top, old_size)); //老的Topchunk的尾部地址/* brk第一次分配返回值 snd_brk第二次分配返回值 */brk snd_brk (char *) (MORECORE_FAILURE); //默认设置分配失败
2. 尝试使用grow_heap函数
如果剩余的空间不足首先通过grow_heap尝试heap区的扩容。
grow_heap函数进行扩容有分配页大小的限制分配页的大小要小于HEAP_MAX_SIZE。
/* 如果不是主分配区则通过heap_info方式获取堆信息结构并且通过MMAP分配内存 */if (av ! main_arena){heap_info *old_heap, *heap;size_t old_heap_size;/* First try to extend the current heap. *//* 通过heap_for_ptr获取当前的heap的数据结构 */old_heap heap_for_ptr (old_top);old_heap_size old_heap-size;/*** 如果剩余的空间不足首先通过grow_heap尝试heap区的扩容* grow_heap函数进行扩容有分配页大小的限制分配页的大小要小于HEAP_MAX_SIZE*/if ((long) (MINSIZE nb - old_size) 0 grow_heap (old_heap, MINSIZE nb - old_size) 0){/* 扩容方式成功 */av-system_mem old_heap-size - old_heap_size; //变更系统内存记录增加部分为新的heap的size减去老的heap的值set_head (old_top, (((char *) old_heap old_heap-size) - (char *) old_top)| PREV_INUSE); //设置p-mchunk_size size 使用中}
3. 尝试使用new_heap函数
扩容失败则new一个新的heap 并将av-top指向到新的heap
new_heap 通过MMAP方式分配一块内存 32位系统每次映射1M64位系统每次映射64M。 /* 扩容失败则new一个新的heap 并将av-top指向到新的heap *//* new_heap 通过MMAP方式分配一块内存 32位系统每次映射1M64位系统每次映射64M*/else if ((heap new_heap (nb (MINSIZE sizeof (*heap)), mp_.top_pad))){/* Use a newly allocated heap. */heap-ar_ptr av; //新的heap指向avheap-prev old_heap; //指向前一个老的heapav-system_mem heap-size; //调整分配区的系统内存/* Set up the new top. *//* #define top(ar_ptr) ((ar_ptr)-top) */top (av) chunk_at_offset (heap, sizeof (*heap)); //减去heap结构的长度就能定位到chunk的偏移量并将new_heap出来的块放置到av-top上set_head (top (av), (heap-size - sizeof (*heap)) | PREV_INUSE); //设置p-mchunk_size/* Setup fencepost and free the old top chunk with a multiple ofMALLOC_ALIGNMENT in size. *//* The fencepost takes at least MINSIZE bytes, because it mightbecome the top chunk again later. Note that a footer is setup, too, although the chunk is marked in use. */old_size (old_size - MINSIZE) ~MALLOC_ALIGN_MASK;set_head (chunk_at_offset (old_top, old_size 2 * SIZE_SZ), 0 | PREV_INUSE);//设置p-mchunk_size size PREV使用中if (old_size MINSIZE){set_head (chunk_at_offset (old_top, old_size), (2 * SIZE_SZ) | PREV_INUSE);//设置p-mchunk_size size PREV使用中set_foot (chunk_at_offset (old_top, old_size), (2 * SIZE_SZ)); //设置p-mchunk_prev_sizeset_head (old_top, old_size | PREV_INUSE | NON_MAIN_ARENA);//设置p-mchunk_size size PREV使用中 非主分配区_int_free (av, old_top, 1); //释放old_top}else{set_head (old_top, (old_size 2 * SIZE_SZ) | PREV_INUSE);//设置p-mchunk_size size PREV使用中set_foot (old_top, (old_size 2 * SIZE_SZ));//设置p-mchunk_prev_size}}
4. 尝试使用try_mmap方式 /* 直接跳转到try_mmap通过MMAP分配一块内存 */else if (!tried_mmap)/* We can at least try to use to mmap memory. */goto try_mmap;}
四、主分配区分配的实现 如果是主分配区则直接扩容Top chunkbrk和mmap方式都有。
1. 设置Top扩容的size值
size 需要等于 nb此次分配的容量 top_pad每次分配扩展值128K MINSIZE对齐字节
如果主分配区都是通过brk方式分配的是连续性分配的可以减去老的Top chunk剩余的old_size值。因为nb大于old_size。size也需要按照pagesize进行页对齐。 else /* av main_arena 如果是主分配区则直接扩容Top chunkbrk和mmap方式都有*/{ /* Request enough space for nb pad overhead *///# define DEFAULT_TOP_PAD 131072 128*1024 mp_.top_pad默认值128K//mp_.top_pad 初始化或扩展堆的时候需要多申请的内存大小size nb mp_.top_pad MINSIZE; //size分配的内存需要加上 对齐的一些字节/*If contiguous, we can subtract out existing space that we hope tocombine with new space. We add it back later only ifwe dont actually get contiguous space.contiguous:用于判断是否为连续brk分配主分配区先减去已经存在的top空间,再向操作系统申请如果是连续brk分配可以减去原有的old_size如果是非连续brk分配后续还会降old_size加回去由于old_size小于nbtop_pad又是128K所以减去之后也有足够的空间存储*/if (contiguous (av))size - old_size;/*Round to a multiple of page size.If MORECORE is not contiguous, this ensures that we only call itwith whole-page arguments. And if MORECORE is contiguous andthis is not first time through, this preserves page-alignment ofprevious calls. Otherwise, we correct to page-align below.*///按照页进行对齐 对齐size ALIGN_UP (size, pagesize);
2. brk分配成功的方式
调用MORECORE进行brk的分配。 //size大于0然后通过系统调用(sbrk)分配size大小的内存if (size 0){brk (char *) (MORECORE (size));LIBC_PROBE (memory_sbrk_more, 2, brk, size);}/* 如果分配成功 */if (brk ! (char *) (MORECORE_FAILURE)){/* Call the morecore hook if necessary. *///分配成功进行原子操作调用__after_morecore_hookvoid (*hook) (void) atomic_forced_read (__after_morecore_hook);if (__builtin_expect (hook ! NULL, 0))(*hook)();}
3. brk分配失败采用MMAP分配
进入mmap之后相当于之前的brk被打断了则此次size需要加上old_size值。
并通过MMAP方式分配一个size大小的内存块。
通过set_noncontiguous函数设置当前分配区的brk分配已经不连续了。 /* Cannot merge with old top, so add its size back in *//* 如果是连续的brk分配主分配区才存在 */if (contiguous (av))size ALIGN_UP (size old_size, pagesize); //调整size大小分配失败之后又把oldsize给加回去了/* If we are relying on mmap as backup, then use larger units *//* brk分配失败后使用MMAP方式作为备用方案如果size小于1M则使用MMAP的默认值1M */if ((unsigned long) (size) (unsigned long) (MMAP_AS_MORECORE_SIZE))size MMAP_AS_MORECORE_SIZE; //MMAP_AS_MORECORE_SIZ 1024 * 1024 1M/* Dont try if size wraps around 0 */if ((unsigned long) (size) (unsigned long) (nb)){/* 这里使用MMAP分配1M的内存块mbrk为MMAP分配后返回的对象 */char *mbrk (char *) (MMAP (0, size, PROT_READ | PROT_WRITE, 0));/* 如果分配成功则处理一下 */if (mbrk ! MAP_FAILED){/* We do not need, and cannot use, another sbrk call to find end */brk mbrk; //调整brk的值snd_brk brk size; //这里设置到尾部值/*Record that we no longer have a contiguous sbrk region.After the first time mmap is used as backup, we do notever rely on contiguous space since this could incorrectlybridge regions.*/set_noncontiguous (av);//主分配区设置不连续的标记因为这里使用了MMAP分配}}}
五、主分配区分配成功后对齐裁剪操作 主分配区分配成功后返回brk值为分配的内存地址。
后续如果是brk连续分配地址是相邻的则直接扩容Top
如果非相邻的则需要将老的Top chunk剩余的空间释放到bins上并且经过一定裁剪和对齐之后返回新的Top chunk。
1. 内存地址连续相邻直接扩容Top /* 分配成功的情况处理 */if (brk ! (char *) (MORECORE_FAILURE)){if (mp_.sbrk_base 0)mp_.sbrk_base brk;av-system_mem size; //调整av的系统内存大小/*If MORECORE extends previous space, we can likewise extend top size.*///判断是否是通过brk分配的用brk分配的并且地址是连续的则直接更新Top chunk即可就是Top chunk的扩容if (brk old_end snd_brk (char *) (MORECORE_FAILURE))set_head (old_top, (size old_size) | PREV_INUSE); //设置p-mchunk_size size PREV使用中
2. brk方式分配地址不相邻情况
新分配的内存地址大于原来的top chunk的结束地址说明地址是不连续的使用MMAP分配的则无法直接扩容Top chunk。
有连续标记说明是brk方式分配的。这里需要将老的Top的空间以及字节对齐的一些冗余空间进行brk二次扩容放到第一次扩容的尾部。
最终我们要返回两个值aligned_brk对齐后的值和snd_brk二次扩容的值 //新分配的内存地址大于原来的top chunk的结束地址说明地址是不连续的使用MMAP分配的则无法直接扩容Top chunkelse{front_misalign 0;end_misalign 0;correction 0;aligned_brk brk;/* handle contiguous cases 有连续标记说明是brk方式分配的 */if (contiguous (av)){/* Count foreign sbrk as system_mem. */if (old_size)av-system_mem brk - old_end; //Topchunk不连续需要记录外部不连续内存的大小/* 分配的mm内存地址需要进行内存对齐front_misalign表示对齐的地址前有多少个可以对齐的字节*/front_misalign (INTERNAL_SIZE_T) chunk2mem (brk) MALLOC_ALIGN_MASK;if (front_misalign 0) //如果大于0说明可以计算这个偏移量调整对齐后的aligned_brk地址{/*Skip over some bytes to arrive at an aligned position.We dont need to specially mark these wasted front bytes.They will never be accessed anyway becauseprev_inuse of av-top (and any chunk created from its start)is always true after initialization.跳过一些字节达到对齐的位置。我们不需要特别标记这些浪费的前字节。*/correction MALLOC_ALIGNMENT - front_misalign; //对齐可调整的字节aligned_brk correction; //aligned_brk往后调整几个字节地址调整后的brk地址}/*If this isnt adjacent to existing space, then we will notbe able to merge with old_top space, so must add to 2nd request.*///correction新扩展出来的chunk需要继续扩容的大小 包含brk对齐后头部减少的字节 老的top的sizecorrection old_size;/* Extend the end address to hit a page boundary */end_misalign (INTERNAL_SIZE_T) (brk size correction); //对齐后的新的top chunk的尾部地址correction (ALIGN_UP (end_misalign, pagesize)) - end_misalign; //尾部地址需要进行一次对齐对齐后是否需要增加字节//所以correctionbrk前置的对齐字节 老的top的size 新的brk尾部的对齐字节//aligned_brk为调整后的新的chunk的起始地址//snd_brk2次扩容操作的返回值assert (correction 0);snd_brk (char *) (MORECORE (correction)); //进行2次扩容操作snd_brk为第二次扩容操作扩容的值correction/*If cant allocate correction, try to at least find out currentbrk. It might be enough to proceed without failing.Note that if second sbrk did NOT fail, we assume that spaceis contiguous with first sbrk. This is a safe assumption unlessprogram is multithreaded but doesnt use locks and a foreign sbrkoccurred between our first and second calls.*/if (snd_brk (char *) (MORECORE_FAILURE)) //扩容失败{correction 0;snd_brk (char *) (MORECORE (0)); //这里又变成尾部值}else{/* Call the morecore hook if necessary. */void (*hook) (void) atomic_forced_read (__after_morecore_hook); //扩容成功if (__builtin_expect (hook ! NULL, 0))(*hook)();}}
3. mmap方式分配地址不相邻情况
这里主要调整字节对齐和偏移量。mmap方式不进行二次扩容。
最终我们要返回两个值aligned_brk对齐后的值和snd_brk不扩容则直接尾部 /* handle non-contiguous cases 没有连续标记的Case使用MMAP分配的情况 */else{if (MALLOC_ALIGNMENT 2 * SIZE_SZ)/* MORECORE/mmap must correctly align */assert (((unsigned long) chunk2mem (brk) MALLOC_ALIGN_MASK) 0);else{front_misalign (INTERNAL_SIZE_T) chunk2mem (brk) MALLOC_ALIGN_MASK;if (front_misalign 0){/*Skip over some bytes to arrive at an aligned position.We dont need to specially mark these wasted front bytes.They will never be accessed anyway becauseprev_inuse of av-top (and any chunk created from its start)is always true after initialization.*/aligned_brk MALLOC_ALIGNMENT - front_misalign; //调整对齐aligned_brk为调整后的起始地址}}/* Find out current end of memory */if (snd_brk (char *) (MORECORE_FAILURE)){snd_brk (char *) (MORECORE (0)); //尾部}}
4. 设置调整后的Topchunk
1. 设置av-top指向调整后的aligned_brk
2. 将老的Top chunk上剩余的空间释放到bins上 /* Adjust top based on results of second sbrk 前面brk也好MMAP也好都成功分配之后的操作*/if (snd_brk ! (char *) (MORECORE_FAILURE)){av-top (mchunkptr) aligned_brk; //将top指向调整过的aligned_brk地址set_head (av-top, (snd_brk - aligned_brk correction) | PREV_INUSE); //设置p-mchunk_size size PREV使用中av-system_mem correction; //系统内存加上correction/*If not the first time through, we either have agap due to foreign sbrk or a non-contiguous region. Insert adouble fencepost at old_top to prevent consolidation with spacewe dont own. These fenceposts are artificial chunks that aremarked as inuse and are in any case too small to use. We needtwo to make sizes and alignments work out.*//* 合并加工后的的old_top需要回收free */if (old_size ! 0){/*Shrink old_top to insert fenceposts, keeping size amultiple of MALLOC_ALIGNMENT. We know there is at leastenough space in old_top to do this.缩小2个字节并重新设置old_top-mchunk_sizeold_size是有足够空间缩小两个字节的*/old_size (old_size - 4 * SIZE_SZ) ~MALLOC_ALIGN_MASK;set_head (old_top, old_size | PREV_INUSE);/*Note that the following assignments completely overwriteold_top when old_size was previously MINSIZE. This isintentional. We need the fencepost, even if old_top otherwise getslost.*/set_head (chunk_at_offset (old_top, old_size),(2 * SIZE_SZ) | PREV_INUSE); //设置set_head (chunk_at_offset (old_top, old_size 2 * SIZE_SZ),(2 * SIZE_SZ) | PREV_INUSE);/* If possible, release the rest. */if (old_size MINSIZE){_int_free (av, old_top, 1); //释放old_top的chunk}}}
六、切割新的Topchunk分配内存 如果Top chunk的大小大于分配的值则进行切割分配操作切割出来的分配出去剩余的remainder变成Top chunk。 /* finally, do the allocation */p av-top;size chunksize (p); //获取Top chunk的size p-mchunk_size/* check that one of the above allocation paths succeeded *//* 如果Top chunk的大小大于分配的值则进行切割分配操作切割出来的分配出去剩余的remainder变成Top chunk*/if ((unsigned long) (size) (unsigned long) (nb MINSIZE)){remainder_size size - nb;remainder chunk_at_offset (p, nb);av-top remainder;set_head (p, nb | PREV_INUSE | (av ! main_arena ? NON_MAIN_ARENA : 0));set_head (remainder, remainder_size | PREV_INUSE);check_malloced_chunk (av, p, nb);return chunk2mem (p);}
至此我们将malloc函数的实现基本讲解完了下一章开始讲解free函数。