ptmalloc源码分析 - Top chunk的扩容函数sysmalloc实现(09)

目录

一、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 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;

sysmalloc基本逻辑:

  1. 非主分配区,通过MMAP并生成heap对象进行Top chunk的扩容操作。
  2. 非主分配区使用MMAP分配,32位操作系统每次分配1M,64位系统每次分配64M
  3. 非主分配区如果分配失败,则调用try_mmap,直接通过MMAP返回需要的内存
  4. 主分配区里面,一般情况下调用brk对Top chunk进行扩容
  5. 主分配区如果通过brk分配失败,则才调用MMAP方式分配
  6. 主分配区brk分配,每次除了需要分配的nb内存字节外,额外还会扩容mp_.top_pad=128K的内存空间;如果主分配区使用MMAP分配,每次分配1M+
  7. 无论主分配区还是非主分配区,分配成功后,都要对分配的内存进行一系列对齐、标记、裁剪、释放等操作
  8. 最后,切割当前需要分配的内存,并将Remainder chunk指向Top chunk地址

 

二、强制try_mmap分配方式


try_mmap是尝试直接进行mmap的方式分配一段内存。当非主分配区通过扩容heap或者new heap都失败的情况下,采用try_mmap方式分配。

但是需要符合MMAP的阀值以及系统支持MMAP,nb分配的内存为大对象(超过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. av==NULL,则直接采用MMAP的方式分配内存2. nb分配的内存为大对象(超过128k),并且符合MMAP的阀值以及系统支持MMAP,则采用MMAP分配mp_.n_mmaps_max=65536mp_.mmap_threshold=128*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;/* Don't 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 mmap'ed 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_size;set_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位系统每次映射1M,64位系统每次映射64M

      /* 扩容失败,则new一个新的heap ;并将av->top指向到新的heap *//* new_heap 通过MMAP方式分配一块内存, 32位系统每次映射1M,64位系统每次映射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 chunk(brk和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 chunk(brk和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 don't actually get contiguous space.contiguous:用于判断是否为连续brk分配,主分配区先减去已经存在的top空间,再向操作系统申请如果是连续brk分配,可以减去原有的old_size如果是非连续brk分配,后续还会降old_size加回去由于old_size小于nb,top_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/* Don't 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 don't 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 isn't 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; //尾部地址需要进行一次对齐,对齐后是否需要增加字节//所以:correction=brk前置的对齐字节 + 老的top的size + 新的brk尾部的对齐字节//aligned_brk:为调整后的新的chunk的起始地址//snd_brk:2次扩容操作的返回值assert (correction >= 0);snd_brk = (char *) (MORECORE (correction)); //进行2次扩容操作,snd_brk为第二次扩容操作,扩容的值:correction/*If can't 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 doesn't 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 don't 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 don't 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_size,old_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函数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/136957.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RK3568开发板SG90 舵机模块的功能实现-迅为电子

1 模块说明 SG90 舵机模块如下图所示: 常见的舵机转向角度有 0-90 度&#xff0c;0-180 度&#xff0c;0-360 度&#xff0c;可以用在垃圾桶项目开盖用&#xff0c;智能小车的全比例转向&#xff0c;摄像头云台&#xff0c;机械臂等。 2 接线说明 SG90 舵机模块上三条线&…

跨境电商运营的新趋势:自养号测评补单技术解析

当前阶段&#xff0c;亚马逊、速卖通、虾皮、lazada等主流跨境电商平台的主要推广方式仍然是广告投放&#xff0c;毕竟这是平台的主要收入来源之一。然而&#xff0c;随着越来越多的卖家进军跨境市场&#xff0c;市场竞争日趋激烈&#xff0c;传统的广告投入效果逐渐减弱。在这…

如何实现一个简单的深度优先搜索(DFS)算法?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 实现深度优先搜索⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前…

【C语言】指针的进阶(一)

目录 前言 1. 字符指针 2. 指针数组 3. 数组指针 3.1 数组指针的定义 3.2 &数组名VS数组名 3.3 数组指针的使用 4. 数组参数、指针参数 4.1 一维数组传参 4.2 二维数组传参 4.3 一级指针传参 4.4 二级指针传参 5. 函数指针 前言 指针在C语言中可谓是有着举足轻重的…

单元测试的重要性:编写更安全、更可靠的代码

在软件开发过程中&#xff0c;测试是非常重要的一环。而在众多的测试方法中&#xff0c;单元测试占据了不可忽视的地位。那么&#xff0c;为什么我们需要进行单元测试呢&#xff1f;以下将从理论和实践两方面进行详细的解释。 一、单元测试的定义和目的 单元测试是指对软件中的…

企业架构LNMP学习笔记45

失效机制&#xff08;了解&#xff09; 1&#xff09;如果key过期了&#xff0c;value会及时删除么&#xff1f;空间会及时清理么&#xff1f; 2&#xff09;如果分配的存储空间&#xff0c;写满了&#xff0c;还允许写么&#xff1f; -m可以配置内存大小。 memcached 内部不…

计算机视觉与深度学习-卷积神经网络-纹理表示卷积神经网络-卷积神经网络-[北邮鲁鹏]

这里写目录标题 参考文章全连接神经网络全连接神经网络的瓶颈全连接神经网络应用场景 卷积神经网络卷积层(CONV)卷积核卷积操作卷积层设计卷积步长(stride)边界填充特征响应图组尺寸计算 激活层池化层(POOL)池化操作定义池化操作作用池化层超参数常见池化操作 全连接层(FC)样本…

【vue3页面展示代码】展示代码codemirror插件

技术版本&#xff1a; vue 3.2.40、codemirror 6.0.1、less 4.1.3、vue-codemirror 6.1.1、 codemirror/lang-vue 0.1.2、codemirror/theme-one-dark 6.1.2 效果图&#xff1a; 1.安装插件 yarn add codemirror vue-codemirror codemirror/lang-vue codemirror/theme-one-dar…

分类预测 | MATLAB实现WOA-CNN-LSTM-Attention数据分类预测

分类预测 | MATLAB实现WOA-CNN-LSTM-Attention数据分类预测 目录 分类预测 | MATLAB实现WOA-CNN-LSTM-Attention数据分类预测分类效果基本描述模型描述程序设计参考资料 分类效果 基本描述 1.MATLAB实现WOA-CNN-LSTM-Attention数据分类预测&#xff0c;运行环境Matlab2021b及以…

VLAN相关知识点

文章目录 前言VLANVLAN数据帧格式QinQ报文封装格式总结 前言 本博客仅做学习笔记&#xff0c;如有侵权&#xff0c;联系后即刻更改 科普&#xff1a; 参考网址 VLAN VLAN&#xff08;Virtual Local Area Network&#xff09;即虚拟局域网 是将一个物理的LAN在逻辑上划分成多…

电视盒子什么品牌好?数码小编盘点网络电视盒子排行榜

电视盒子什么品牌好&#xff1f;每个品牌的优势并不一样&#xff0c;我们要根据自己的需求选择&#xff0c;看视频选无广告的&#xff0c;投屏频繁选投屏功能完善的&#xff0c;不懂的新手们可以参考小编分享的网络电视盒子排行榜&#xff0c;堪称目前最专业权威的电视盒子排名…

el-image 和 el-table冲突层级冲突问题

其中原理&#xff0c;很多博客已经所过了&#xff0c;table组件中使用图片&#xff0c;会出现层级过低问题&#xff0c; 网上大部分解决方式是 使用穿透 // 单元格样式 ::v-deep(.el-table__cell) {position: static !important; }我在此不推荐这种解决方式&#xff0c;原因&a…

亚马逊,沃尔玛,速卖通卖家如何实现高效且安全的测评补单

对于卖家而言&#xff0c;测评资源确实是一项宝贵的财富。通过自养号和广告相结合的策略&#xff0c;可以快速有效地提升产品销量、增加转化率以及提高Listing的权重。这是一个很好的方法来优化产品在平台上的表现 现在很多卖家都选择自己养号下单&#xff0c;自己做测评 1.可…

ESD最常用的3种模型?|深圳比创达EMC

为了定量表征 ESD的特性&#xff0c;一般将 ESD 转化成模型表达方式&#xff0c;ESD 的模型有很多种&#xff0c;下面介绍最常用的三种。 1.HBM&#xff1a;Human Body Model&#xff0c;人体模型 该模型表征人体带电接触器件放电&#xff0c;Rb 为等效人体电阻&#xff0c;Cb…

6-1 汉诺塔

汉诺&#xff08;Hanoi&#xff09;塔问题是一个经典的递归问题。 设有A、B、C三个塔座&#xff1b;开始时&#xff0c;在塔座A上有若干个圆盘&#xff0c;这些圆盘自下而上&#xff0c;由大到小地叠在一起。要求将塔座A上的圆盘移到塔座B上&#xff0c;并仍按同样顺序叠放。在…

【论文检索】待更新补充

&#xff08;一&#xff09;相关网址&#xff1a; 1.谷歌学术镜像网址&#xff1a;dailyheadlines.cc 能查到年限、引用量、发表的期刊 2.dblp: 看不到pdf&#xff0c;可以用于查某个作者最近几年都发表了什么论文 作者消歧&#xff1a;通过邮箱和机构判断是不是同一个人 …

亚马逊、ozon、阿里狗等平台如何获取销量和评价?

在亚马逊、Ozon和Allegro上有很多商家还在沿用这国内电商平台的那一套玩法&#xff0c;给店铺不断的做测评&#xff0c;以此提高店铺的流量和销量等&#xff0c;具体应该怎么做测评呢? 方法一&#xff1a;找站点当地的外国人帮助进行产品的测评&#xff0c;这可以通过Faceboo…

ISP技术概述

原本或许是为了对冲手机系统和APP设计无力感而诞生的拍照功能,现今却成为了众手机厂家除背部设计外为数不多可“卷”的地方,自拍、全景、夜景、小视频等旺盛的需求让这一技术的江湖地位迅速变化。对圈内人士而言,这一波变化带来的后摄、双摄、多摄、暗光、防抖、广角、长焦、…

C# Onnx Yolov8 Detect 物体检测

效果 项目 代码 using Microsoft.ML.OnnxRuntime; using Microsoft.ML.OnnxRuntime.Tensors; using OpenCvSharp; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System…

C#使用DirectX SDK 加载.x三维模型

最近因为项目要做显示一个三维模型&#xff0c;所以研究了下如何在Winform中加载并显示三维模型。在Windows平台巨硬公司提供了DirectX SDK用于渲染图形&#xff0c;参考了几篇文章做了个demo记录下&#xff0c;以便日后温习只用。这个SDK涉及到了计算机图形学的一些基础知识&a…