关闭
Hit
enter
to search or
ESC
to close
May I Suggest ?
#leanote #leanote blog #code #hello world
Mutepig's Blog
Home
Archives
Tags
Search
About Me
Linux堆基础知识(2)
无
863
1
3
mut3p1g
通过上一节的分析,已经对堆的基本结构有了大致的了解,那么下面就是来看看其关键的操作:内存分配及回收。由于分配的实现代码太长了,将在后面进行分析,这一节主要对内存回收进行分析。 <!--more--> ## 0x01 分配区的初始化 上一节最后提到了一个结构体`malloc_state`,用来分配区,而其简化后就是`mstate`。 ``` 13 typedef struct malloc_state *mstate; ``` 然后就是分配区实例`av`的初始化了 ``` 1813 static void 1814 malloc_init_state (mstate av) 1815 { 1816 int i; 1817 mbinptr bin; 1818 1819 /* Establish circular links for normal bins */ 1820 for (i = 1; i < NBINS; ++i) // 首先遍历bins 1821 { 1822 bin = bin_at (av, i); // 获取每个bin的链表 1823 bin->fd = bin->bk = bin; // 初始化每个链表为空,即头尾都指向自己 1824 } 1825 1826 #if MORECORE_CONTIGUOUS 1827 if (av != &main_arena) 1828 #endif 1829 set_noncontiguous (av); // 只有主分配才能分配连续的虚拟地址空间,所以对于非主分区需要设置为非连续的虚拟地址空间 1830 if (av == &main_arena) // 如果是主分区,则需要初始化fastbins,且只会初始化这么一次,因为每个进程只有一个主分配区 1831 set_max_fast (DEFAULT_MXFAST); 1832 av->flags |= FASTCHUNKS_BIT; // 表示存在fastbins 1833 1834 av->top = initial_top (av); // 初始化top 1835 } ``` ## 0x02 其他函数 ### 1. malloc_consolidate 这个函数的作用就是将`fastbin`合并后置入`unsorted bin`,一般调用的情况有以下几种: ``` 1. free某chunk时,该chunk合并前后空闲块后的大小超过了fastbin的收缩阈值。(一般与top合并时会触发) 2. malloc的大小在smallbin范围内,若对应的smallbin没初始化的时候。 3. malloc的大小在largebin范围内。 4. 当各种bins(包括topchunk)都无法满足malloc的要求时,如果发现仍然存在fastbin,则需要合并fastbin并重新malloc一次。 ``` 同时有下面两点需要注意的: ``` 1. malloc_consolidate在合并fastbin的过程中没有对其size进行校验 2. malloc_consolidate将合并后生成的chunk插入到unsorted bin头部 ``` #### 1) 初始化 首先通过判断`get_max_fast`是否为0来判断`ptmalloc`是否初始化了,如果没有还需要初始化。之后获取`unsorted bin`,接下来要用。 ``` 4429 static void malloc_consolidate(mstate av) 4430 { 4431 mfastbinptr* fb; /* current fastbin being consolidated */ 4432 mfastbinptr* maxfb; /* last fastbin (for loop control) */ 4433 mchunkptr p; /* current chunk being consolidated */ 4434 mchunkptr nextp; /* next chunk to consolidate */ 4435 mchunkptr unsorted_bin; /* bin header */ 4436 mchunkptr first_unsorted; /* chunk to link to */ 4437 4438 /* These have same use as in free() */ 4439 mchunkptr nextchunk; 4440 INTERNAL_SIZE_T size; 4441 INTERNAL_SIZE_T nextsize; 4442 INTERNAL_SIZE_T prevsize; 4443 int nextinuse; 4444 mchunkptr bck; 4445 mchunkptr fwd; 4446 4447 /* 4448 If max_fast is 0, we know that av hasn't 4449 yet been initialized, in which case do so below 4450 */ 4451 4452 if (get_max_fast () != 0) { // 当前分配区已初始化 4453 clear_fastchunks(av); // 清除当前分配区存在fastbin的标志,因为后面就会把所有的fastbin全加入unsorted bin中了 4454 4455 unsorted_bin = unsorted_chunks(av); // 获得unsorted bin ..... 4520 } 4521 else { 4522 malloc_init_state(av); // 初始化分配区 4523 check_malloc_state(av); 4524 } 4525 } ``` #### 2) 将fastbin与前后空闲块合并 合并的过程和之前分析的合并也一样,判断每个`fastbin`前后是否空闲,然后进行合并。 ``` 4457 /* 4458 Remove each chunk from fast bin and consolidate it, placing it 4459 then in unsorted bin. Among other reasons for doing this, 4460 placing in unsorted bin avoids needing to calculate actual bins 4461 until malloc is sure that chunks aren't immediately going to be 4462 reused anyway. 4463 */ 4464 4465 maxfb = &fastbin (av, NFASTBINS - 1); //获取最大的fastbin 4466 fb = &fastbin (av, 0); // 获得最小的fastbins 4467 do { 4468 p = atomic_exchange_acq (fb, NULL); // p指向fb的第一个fastbin 4469 if (p != 0) { // 如果当前分配区存在fastbin 4470 do { 4471 check_inuse_chunk(av, p); // 判断p是否在使用中(因为free一个fastbin时并不会将它的inuse位去掉) 4472 nextp = p->fd;// 获得p的下一个chunk的指针 4473 4474 /* Slightly streamlined version of consolidation code in free() */ 4475 size = chunksize (p); 4476 nextchunk = chunk_at_offset(p, size); 4477 nextsize = chunksize(nextchunk); 4478 4479 if (!prev_inuse(p)) { //如果p的上一个chunk也不在使用中则合并 4480 prevsize = prev_size (p); 4481 size += prevsize; 4482 p = chunk_at_offset(p, -((long) prevsize)); 4483 unlink(av, p, bck, fwd); 4484 } 4485 4486 if (nextchunk != av->top) { 4487 nextinuse = inuse_bit_at_offset(nextchunk, nextsize); 4488 4489 if (!nextinuse) { //如果p的下一个chunk不在使用中且不是topchunk,则合并 4490 size += nextsize; 4491 unlink(av, nextchunk, bck, fwd); 4492 } else 4493 clear_inuse_bit_at_offset(nextchunk, 0); // 标记下一块不在使用中了 4494 ....... 4508 } 4509 4510 else { // 如果下一块是topchunk 4511 size += nextsize; 4512 set_head(p, size | PREV_INUSE); 4513 av->top = p; 4514 } 4515 4516 } while ( (p = nextp) != 0); // fastbin循环处理 4517 4518 } 4519 } while (fb++ != maxfb); ``` #### 3) 将合并后的chunk加入unsorted bin 这个插入操作也是和之前一样的,将合并后的`chunk`插入到`unsorted bin`的第一个,同时设置一下相关标志。 ``` 4495 first_unsorted = unsorted_bin->fd; // 获得第一个unsorted bin 4496 unsorted_bin->fd = p; // 将当前p插入为第一个unsorted chunk 4497 first_unsorted->bk = p; 4498 4499 if (!in_smallbin_range (size)) { // 如果合并后的chunk位large bin则还需设置size链 4500 p->fd_nextsize = NULL; 4501 p->bk_nextsize = NULL; 4502 } 4503 4504 set_head(p, size | PREV_INUSE); // 设置当前chunk的前一chunk为使用中 4505 p->bk = unsorted_bin; 4506 p->fd = first_unsorted; 4507 set_foot(p, size); ``` ### 2. unlink `unlink`的作用就是将某个`chunk`从它所在的链表中取出来,由于它知道自己的双向链表中前面和后面的`chunk`的地址,所以直接将前后两个`chunk`的指向更改一下就可以了。 ``` 1404 #define unlink(AV, P, BK, FD) { \ 1405 if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0)) \ // 当前chunk的size得和下一个chunk记录的prev_size相同 1406 malloc_printerr (check_action, "corrupted size vs. prev_size", P, AV); \ 1407 FD = P->fd; \ 1408 BK = P->bk; \ 1409 if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \//防止doublefree 1410 malloc_printerr (check_action, "corrupted double-linked list", P, AV); \ 1411 else { \ 1412 FD->bk = BK; \ 1413 BK->fd = FD; \ 1414 if (!in_smallbin_range (chunksize_nomask (P)) \ //如果是largebin,下面就是将chunk从这条链中去除 1415 && __builtin_expect (P->fd_nextsize != NULL, 0)) { \ 1416 if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0) \ 1417 || __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0)) \ 1418 malloc_printerr (check_action, \ 1419 "corrupted double-linked list (not small)", \ 1420 P, AV); \ 1421 if (FD->fd_nextsize == NULL) { \ 1422 if (P->fd_nextsize == P) \ 1423 FD->fd_nextsize = FD->bk_nextsize = FD; \ 1424 else { \ 1425 FD->fd_nextsize = P->fd_nextsize; \ 1426 FD->bk_nextsize = P->bk_nextsize; \ 1427 P->fd_nextsize->bk_nextsize = FD; \ 1428 P->bk_nextsize->fd_nextsize = FD; \ 1429 } \ 1430 } else { \ 1431 P->fd_nextsize->bk_nextsize = P->bk_nextsize; \ 1432 P->bk_nextsize->fd_nextsize = P->fd_nextsize; \ 1433 } \ 1434 } \ 1435 } \ 1436 } ``` ## 0x03 内存释放 由于`free`的代码稍微短一些所以我这里先分析内存释放,其核心代码主要是`__libc_free`,下面开始分析。 ### 1. __libc_free ``` 3090 void 3091 __libc_free (void *mem) // 首先需要注意free的是mem不是chunk,即malloc后返回的地址 3092 { 3093 mstate ar_ptr; 3094 mchunkptr p; /* chunk corresponding to mem */ 3095 3096 void (*hook) (void *, const void *) 3097 = atomic_forced_read (__free_hook); 3098 if (__builtin_expect (hook != NULL, 0)) 3099 { 3100 (*hook)(mem, RETURN_ADDRESS (0)); 3101 return; 3102 } 3103 3104 if (mem == 0) /* free(0) has no effect */ 3105 return; 3106 3107 p = mem2chunk (mem);// 将mem转为chunk 3108 3109 if (chunk_is_mmapped (p)) //如果待释放的chunk是通过mmap申请的 /* release mmapped memory. */ 3110 { 3111 /* See if the dynamic brk/mmap threshold needs adjusting. 3112 Dumped fake mmapped chunks do not affect the threshold. */ 3113 if (!mp_.no_dyn_threshold // mmap的阈值是动态的 3114 && chunksize_nomask (p) > mp_.mmap_threshold // 当前free的chunk大小超过了原来的阈值 3115 && chunksize_nomask (p) <= DEFAULT_MMAP_THRESHOLD_MAX // 当前free的chunk大小小于阈值的最大值(4*1024*1024*sizeof(long)) 3116 && !DUMPED_MAIN_ARENA_CHUNK (p)) // 当前chunk不在dumped_main_arena里 3117 { 3118 mp_.mmap_threshold = chunksize (p); // 更新mmap_threshold为当前free的chunk的大小 3119 mp_.trim_threshold = 2 * mp_.mmap_threshold; // 更新trim_threshold为mmap_threshold的两倍 3120 LIBC_PROBE (memory_mallopt_free_dyn_thresholds, 2, 3121 mp_.mmap_threshold, mp_.trim_threshold); 3122 } 3123 munmap_chunk (p); // 释放mmap申请的空间 3124 return; 3125 } 3126 3127 MAYBE_INIT_TCACHE (); 3128 3129 ar_ptr = arena_for_chunk (p); // 获得p对应的分配区 3130 _int_free (ar_ptr, p, 0); // 释放p 3131 } ``` 对这个函数总结一下: 1. 首先需要获取分配区的锁,以保证安全。 2. 接着将待释放的`mem`转换为`chunk`,方便处理。 3. 对通过`mmap`申请的空间调用`munmap`函数进行释放,同时对`mmap`分配`mmap_threshold`及收缩`trim_threshold`阈值进行调整。 4. 若不是通过`mmap`分配的空间,则调用`_int_free`进行释放。 ### 2. _int_free #### 1) 变量申明 下面都是后面会用上的临时变量,到时候用到了可以回来看看定义。 ``` 4132 static void 4133 _int_free (mstate av, mchunkptr p, int have_lock) 4134 { 4135 INTERNAL_SIZE_T size; /* its size */ 4136 mfastbinptr *fb; /* associated fastbin */ 4137 mchunkptr nextchunk; /* next contiguous chunk */ 4138 INTERNAL_SIZE_T nextsize; /* its size */ 4139 int nextinuse; /* true if nextchunk is used */ 4140 INTERNAL_SIZE_T prevsize; /* size of previous contiguous chunk */ 4141 mchunkptr bck; /* misc temp for linking */ 4142 mchunkptr fwd; /* misc temp for linking */ 4143 4144 const char *errstr = NULL; 4145 int locked = 0; 4146 4147 size = chunksize (p); // 首先获取待释放空间的大小 ``` #### 2) 安全检查 这里主要做了下面三个安全检查: 1. p的地址是否合法,是否对齐 2. p的`size`是否大于等于`MINSIZE`且对齐 3. p的下一个`chunk`的`PREV_INUSE`是否为真 ``` 4149 /* Little security check which won't hurt performance: the 4150 allocator never wrapps around at the end of the address space. 4151 Therefore we can exclude some size values which might appear 4152 here by accident or by "design" from some intruder. */ 4153 if (__builtin_expect ((uintptr_t) p > (uintptr_t) -size, 0) // 指针地址不能溢出 4154 || __builtin_expect (misaligned_chunk (p), 0)) // chunk没有对齐 4155 { 4156 errstr = "free(): invalid pointer"; 4157 errout: 4158 if (!have_lock && locked) 4159 __libc_lock_unlock (av->mutex); 4160 malloc_printerr (check_action, errstr, chunk2mem (p), av); 4161 return; 4162 } 4163 /* We know that each chunk is at least MINSIZE bytes in size or a 4164 multiple of MALLOC_ALIGNMENT. */ 4165 if (__glibc_unlikely (size < MINSIZE || !aligned_OK (size))) // chunk的大小小于MINSIZE或者没有对齐 4166 { 4167 errstr = "free(): invalid size"; 4168 goto errout; 4169 } 4170 4171 check_inuse_chunk(av, p); // 判断chunk是否在使用中(我们释放的必须是在使用中的) 4172 ``` #### 3) fastbin ##### a. fastbin check 前提就是当前待释放的`chunk`属于`fastbin`,且它的下一个`chunk`不能是`top-chunk`。 ``` 4192 if ((unsigned long)(size) <= (unsigned long)(get_max_fast ()) // chunk大小小于fastbin的最大值 4193 4194 #if TRIM_FASTBINS 4195 /* 4196 If TRIM_FASTBINS set, don't place chunks 4197 bordering top into fastbins 4198 */ 4199 && (chunk_at_offset(p, size) != av->top) // chunk的下一个不能是top 4200 #endif 4201 ) { ``` ##### b. next_chunk check & lock 下一个`chunk`的大小不能太小,也不能太大。关于锁的部分我也不太懂,所以不具体分析了。 ``` 4203 if (__builtin_expect (chunksize_nomask (chunk_at_offset (p, size)) 4204 <= 2 * SIZE_SZ, 0)//太小 4205 || __builtin_expect (chunksize (chunk_at_offset (p, size)) 4206 >= av->system_mem, 0))//太大 4207 { 4208 /* We might not have a lock at this point and concurrent modifications 4209 of system_mem might have let to a false positive. Redo the test 4210 after getting the lock. */ 4211 if (have_lock 4212 || ({ assert (locked == 0); 4213 __libc_lock_lock (av->mutex); 4214 locked = 1; 4215 chunksize_nomask (chunk_at_offset (p, size)) <= 2 * SIZE_SZ 4216 || chunksize (chunk_at_offset (p, size)) >= av->system_mem; 4217 })) 4218 { 4219 errstr = "free(): invalid next size (fast)"; 4220 goto errout; 4221 } 4222 if (! have_lock) 4223 { 4224 __libc_lock_unlock (av->mutex); 4225 locked = 0; 4226 } 4227 } ``` ##### c. Init for fastbin ``` 1904 free_perturb (char *p, size_t n) 1905 { 1906 if (__glibc_unlikely (perturb_byte)) 1907 memset (p, perturb_byte, n); 1908 } 4229 free_perturb (chunk2mem(p), size - 2 * SIZE_SZ); // 如果设置了perturb_byte,则将fastbin中的content全部赋值为perturb_byte 4230 4231 set_fastchunks(av); // 设置分配区av中存在空闲fastbin的 4232 unsigned int idx = fastbin_index(size); // 获取待释放chunk对应的fastbin_index 4233 fb = &fastbin (av, idx); // 获取待释放chunk对应的fastbin ``` ##### d. link p to its fastbin 这里`fastbin`的处理使用了`lock-free`技术,而该技术基于`CAS(compare-and-swap)`原子操作。具体算法大家可以学习一下。这里将释放的`chunk`加入到了对应大小的`fastbins`的第一个。当释放的是对应`fastbins`的链表头的话,则会报`double free`错误;否则可以多次释放,也可以多次分配。 ``` 4235 /* Atomically link P to its fastbin: P->FD = *FB; *FB = P; */ 4236 mchunkptr old = *fb, old2; // old指对应fastbins的链表头 4237 unsigned int old_idx = ~0u; 4238 do 4239 { 4240 /* Check that the top of the bin is not the record we are going to add 4241 (i.e., double free). */ 4242 if (__builtin_expect (old == p, 0)) // 这里只判断了当前释放的是不是fastbins对应的链表头 4243 { 4244 errstr = "double free or corruption (fasttop)"; 4245 goto errout; 4246 } 4247 /* Check that size of fastbin chunk at the top is the same as 4248 size of the chunk that we are adding. We can dereference OLD 4249 only if we have the lock, otherwise it might have already been 4250 deallocated. See use of OLD_IDX below for the actual check. */ 4251 if (have_lock && old != NULL) 4252 old_idx = fastbin_index(chunksize(old)); 4253 p->fd = old2 = old; 4254 } 4255 while ((old = catomic_compare_and_exchange_val_rel (fb, p, old2)) != old2); ``` e. last check 最后一个主要是要求表头和释放的`chunk`所属的`fastbin`相同。 ``` 4257 if (have_lock && old != NULL && __builtin_expect (old_idx != idx, 0)) 4258 { 4259 errstr = "invalid fastbin entry (free)"; 4260 goto errout; 4261 } 4262 } ``` #### 4) unsorted bin 对于不是`mmap`分配且不是`fastbin`的空间,释放后都会直接加入`unsorted bin`。 ##### a. check 这里主要有这么几个判断: 1. 当前释放的`chunk`不能是`topchunk`,因为`topchunk`本来就是空闲的。 2. 当前释放的`chunk`的下一个`chunk`不能超过`topchunk`的结束地址。 3. 通过查看下一个`chunk`的`prev_size`,发现当前当前`chunk`并不在使用状态。 4. 下一个`chunk`的大小过大或过小,不符合系统设置。 ``` 4264 /* 4265 Consolidate other non-mmapped chunks as they arrive. 4266 */ 4267 4268 else if (!chunk_is_mmapped(p)) { // 当前chunk不是通过mmap申请的 4269 if (! have_lock) { 4270 __libc_lock_lock (av->mutex); 4271 locked = 1; 4272 } 4273 4274 nextchunk = chunk_at_offset(p, size); 4275 4276 /* Lightweight tests: check whether the block is already the 4277 top block. */ 4278 if (__glibc_unlikely (p == av->top))// 当前释放的chunk不是topchunk 4279 { 4280 errstr = "double free or corruption (top)"; 4281 goto errout; 4282 } 4283 /* Or whether the next chunk is beyond the boundaries of the arena. */ 4284 if (__builtin_expect (contiguous (av) 4285 && (char *) nextchunk 4286 >= ((char *) av->top + chunksize(av->top)), 0))// 下一个chunk超过了top的结束地址 4287 { 4288 errstr = "double free or corruption (out)"; 4289 goto errout; 4290 } 4291 /* Or whether the block is actually not marked used. */ 4292 if (__glibc_unlikely (!prev_inuse(nextchunk))) // 当前chunk并不在使用中 4293 { 4294 errstr = "double free or corruption (!prev)"; 4295 goto errout; 4296 } 4297 4298 nextsize = chunksize(nextchunk); 4299 if (__builtin_expect (chunksize_nomask (nextchunk) <= 2 * SIZE_SZ, 0) 4300 || __builtin_expect (nextsize >= av->system_mem, 0))// 下一个chunk的大小不合法 4301 { 4302 errstr = "free(): invalid next size (normal)"; 4303 goto errout; 4304 } ``` ##### b. consolidate backward 这里主要是判断前一个`chunk`是否在使用中,如果不在则进行合并,同时将地址设置为前一个`chunk`的地址。 ``` 4306 free_perturb (chunk2mem(p), size - 2 * SIZE_SZ); // 同fastbin相应处理 4307 4308 /* consolidate backward */ 4309 if (!prev_inuse(p)) { // 如果上一个chunk空闲则合并 4310 prevsize = prev_size (p); 4311 size += prevsize; // size加起来 4312 p = chunk_at_offset(p, -((long) prevsize)); // 将p设置为上一个chunk的地址 4313 unlink(av, p, bck, fwd); // 调用unlink进行合并 4314 } ``` ##### c. consolidate forward 这里主要是判断后一个`chunk`是否在使用中,如果不在则进行合并。 ``` 4316 if (nextchunk != av->top) { // 待释放的chunk不与top相邻 4317 /* get and clear inuse bit */ 4318 nextinuse = inuse_bit_at_offset(nextchunk, nextsize); // 查看下一个chunk是否在使用中 4319 4320 /* consolidate forward */ 4321 if (!nextinuse) { // 如果不在使用中则进行合并 4322 unlink(av, nextchunk, bck, fwd); 4323 size += nextsize; //size加起来 4324 } else 4325 clear_inuse_bit_at_offset(nextchunk, 0); // 如果下一个chunk在使用中,就把设置其上一块为空闲 4326 ``` ##### d. place the chunk=> unsorted bin 由于`ptmalloc`设计思路就是不会存在连续两块`chunk`是空闲的,所以在释放某个`chunk`的时候只需要查看其前两个`chunk`是否空闲并合并就可以了。合并完成后就要将其插入为`unsorted bin`的第一个`bin`。 ``` 4327 /* 4328 Place the chunk in unsorted chunk list. Chunks are 4329 not placed into regular bins until after they have 4330 been given one chance to be used in malloc. 4331 */ 4332 4333 bck = unsorted_chunks(av); // 获得当前分配区的unsorted bin 4334 fwd = bck->fd; // 获得unsorted bin的第一个 4335 if (__glibc_unlikely (fwd->bk != bck)) 4336 { 4337 errstr = "free(): corrupted unsorted chunks"; 4338 goto errout; 4339 } 4340 p->fd = fwd; // 将p插入到第一个 4341 p->bk = bck; 4342 if (!in_smallbin_range(size)) // 如果是largebin 4343 { 4344 p->fd_nextsize = NULL; 4345 p->bk_nextsize = NULL; 4346 } 4347 bck->fd = p; 4348 fwd->bk = p; 4349 4350 set_head(p, size | PREV_INUSE); // 设置当前空闲chunk的上一块是在使用中 4351 set_foot(p, size); // 通过设置当前chunk下一块的prev_inuse,标记当前chunk是空闲的 4352 4353 check_free_chunk(av, p); 4354 } 4355 ``` #### 5) topchunk 如果待释放`chunk`紧贴着`topchunk`,则与`top`合并,并将`top`设置为当前`chunk`。这里没有清空。 ``` 4356 /* 4357 If the chunk borders the current high end of memory, 4358 consolidate into top 4359 */ 4360 4361 else { 4362 size += nextsize; 4363 set_head(p, size | PREV_INUSE); 4364 av->top = p; // top设置为当前待释放的chunk 4365 check_chunk(av, p); 4366 } 4367 ``` #### 6) trim heap 下面对堆进行收缩,主要有下面几个操作: 1. 如果释放的空间大于`fastbin`的收缩阈值且`fastbin`不为空,则会调用`malloc_consolidate`将`fastbin`合并且放入`unsorted bin`。 2. 如果是主分配区,且释放的空间大于系统内存收缩阈值,则调用`systrim`收缩系统内存。 3. 如果是非主分区,则始终调用`heap_trim`收缩堆。 ``` 4368 /* 4369 If freeing a large space, consolidate possibly-surrounding 4370 chunks. Then, if the total unused topmost memory exceeds trim 4371 threshold, ask malloc_trim to reduce top. 4372 4373 Unless max_fast is 0, we don't know if there are fastbins 4374 bordering top, so we cannot tell for sure whether threshold 4375 has been reached unless fastbins are consolidated. But we 4376 don't want to consolidate on each free. As a compromise, 4377 consolidation is performed if FASTBIN_CONSOLIDATION_THRESHOLD 4378 is reached. 4379 */ 4380 4381 if ((unsigned long)(size) >= FASTBIN_CONSOLIDATION_THRESHOLD) { 4382 if (have_fastchunks(av)) // 如果size超过了fastbin的合并阈值(默认64KB),且fastbin不为空,则调用malloc_consolidate合并fastbin 4383 malloc_consolidate(av); 4384 4385 if (av == &main_arena) { // 如果是主分配区 4386 #ifndef MORECORE_CANNOT_TRIM 4387 if ((unsigned long)(chunksize(av->top)) >= 4388 (unsigned long)(mp_.trim_threshold)) //如果size超过了系统内存收缩阈值则调用systrim收缩 4389 systrim(mp_.top_pad, av); 4390 #endif 4391 } else { // 如果是非主分配区,则始终调用heap_trim收缩堆 4392 /* Always try heap_trim(), even if the top chunk is not 4393 large, because the corresponding heap might go away. */ 4394 heap_info *heap = heap_for_ptr(top(av)); 4395 4396 assert(heap->ar_ptr == av); 4397 heap_trim(heap, mp_.top_pad); 4398 } 4399 } 4400 4401 if (! have_lock) { 4402 assert (locked); 4403 __libc_lock_unlock (av->mutex); 4404 } 4405 } ``` #### 7) mmap 对于`mmap`申请的空间,则调用`munmap_chunk`释放。 ``` 4410 else { 4411 munmap_chunk (p); 4412 } 4413 } ``` #### 8) 总结 `free`的实现相对比较简单,主要就是对`fastbin`和`unsorted bin`的处理,下面对这两个的校验和流程进行一下总结。 ##### a. 通用检查 1. p的地址是否合法,是否对齐 2. p的`size`是否大于等于`MINSIZE`且对齐 3. p的下一个`chunk`的`PREV_INUSE`是否为真 4. p的下一个`chunk`不能太小(≤2*SIZE_SZ)或太大(≥av->system_mem) ##### b. fastbin * 检查 1. p的大小小于`fastbin`的最大值`global_max_fast` 2. p不能是对应`fastbins`的`fasttop`(应该是将fastbin全部遍历一遍看看在不在里面) 3. p对应`fastbins`的`fasttop`下标与p下标相同 * 流程 通过校验后,p成为对应`fastbins`的`fasttop`,并不修改p下一个`chunk`的`PREV_INUSE`。 ##### c. unsorted bin * 检查 1. p不能是`topchunk` 2. p的下一个`chunk`不能超过`topchunk`的结束地址 * 流程 ![](https://leanote.com/api/file/getImage?fileId=59ce62bbab6441728f0035f2) 最后将合并的`chunk`插入为`unsorted bin`的第一个块 ##### d. next chunk => top 如果下一个`chunk`为`topchunk`,那么直接将两者合并成一个`chunk`,并将新的`chunk`设置为`topchunk`
觉得不错,点个赞?
提交评论
Sign in
to leave a comment.
No Leanote account ?
Sign up now
.
3
条评论
More...
文章目录
No Leanote account ? Sign up now.