for(;;){intiters=0;while((victim=unsorted_chunks(av)->bk)!=unsorted_chunks(av)){bck=victim->bk;size=chunksize(victim);mchunkptrnext=chunk_at_offset(victim,size);...// 当前 chunk 无法满足
/* remove from unsorted list */if(__glibc_unlikely(bck->fd!=victim))malloc_printerr("malloc(): corrupted unsorted chunks 3");unsorted_chunks(av)->bk=bck;bck->fd=unsorted_chunks(av);/* Take now instead of binning if exact fit */if(size==nb){...}/* place chunk in bin */if(in_smallbin_range(size)){...}else{victim_index=largebin_index(size);bck=bin_at(av,victim_index);fwd=bck->fd;/* maintain large bins in sorted order */if(fwd!=bck){/* Or with inuse bit to speed comparisons */size|=PREV_INUSE;/* if smaller than smallest, bypass loop below */assert(chunk_main_arena(bck->bk));if((unsignedlong)(size)<(unsignedlong)chunksize_nomask(bck->bk)){fwd=bck;bck=bck->bk;victim->fd_nextsize=fwd->fd;victim->bk_nextsize=fwd->fd->bk_nextsize;fwd->fd->bk_nextsize=victim->bk_nextsize->fd_nextsize=victim;}else{assert(chunk_main_arena(fwd));while((unsignedlong)size<chunksize_nomask(fwd)){fwd=fwd->fd_nextsize;assert(chunk_main_arena(fwd));}if((unsignedlong)size==(unsignedlong)chunksize_nomask(fwd))/* Always insert in the second position. */fwd=fwd->fd;else{victim->fd_nextsize=fwd;victim->bk_nextsize=fwd->bk_nextsize;if(__glibc_unlikely(fwd->bk_nextsize->fd_nextsize!=fwd))malloc_printerr("malloc(): largebin double linked list corrupted (nextsize)");fwd->bk_nextsize=victim;victim->bk_nextsize->fd_nextsize=victim;}bck=fwd->bk;if(bck->fd!=fwd)malloc_printerr("malloc(): largebin double linked list corrupted (bk)");}}elsevictim->fd_nextsize=victim->bk_nextsize=victim;}mark_bin(av,victim_index);victim->bk=bck;victim->fd=fwd;fwd->bk=victim;bck->fd=victim;}}
/*
If a large request, scan through the chunks of current bin in
sorted order to find smallest that fits. Use the skip list for this.
*/if(!in_smallbin_range(nb)){bin=bin_at(av,idx);/* skip scan if empty or largest chunk is too small */if((victim=first(bin))!=bin&&(unsignedlong)chunksize_nomask(victim)>=(unsignedlong)(nb)){victim=victim->bk_nextsize;while(((unsignedlong)(size=chunksize(victim))<(unsignedlong)(nb)))victim=victim->bk_nextsize;/* Avoid removing the first entry for a size so that the skip
list does not have to be rerouted. */if(victim!=last(bin)&&chunksize_nomask(victim)==chunksize_nomask(victim->fd))victim=victim->fd;remainder_size=size-nb;unlink_chunk(av,victim);/* Exhaust */if(remainder_size<MINSIZE){set_inuse_bit_at_offset(victim,size);if(av!=&main_arena)set_non_main_arena(victim);}/* Split */else{remainder=chunk_at_offset(victim,nb);/* We cannot assume the unsorted list is empty and therefore
have to perform a complete insert here. */bck=unsorted_chunks(av);fwd=bck->fd;if(__glibc_unlikely(fwd->bk!=bck))malloc_printerr("malloc(): corrupted unsorted chunks");remainder->bk=bck;remainder->fd=fwd;bck->fd=remainder;fwd->bk=remainder;if(!in_smallbin_range(remainder_size)){remainder->fd_nextsize=NULL;remainder->bk_nextsize=NULL;}set_head(victim,nb|PREV_INUSE|(av!=&main_arena?NON_MAIN_ARENA:0));set_head(remainder,remainder_size|PREV_INUSE);set_foot(remainder,remainder_size);}check_malloced_chunk(av,victim,nb);void*p=chunk2mem(victim);alloc_perturb(p,bytes);returnp;}}
victim=victim->bk_nextsize;while(((unsignedlong)(size=chunksize(victim))<(unsignedlong)(nb)))victim=victim->bk_nextsize;/* Avoid removing the first entry for a size so that the skip
list does not have to be rerouted. */if(victim!=last(bin)&&chunksize_nomask(victim)==chunksize_nomask(victim->fd))victim=victim->fd;remainder_size=size-nb;unlink_chunk(av,victim);
/* Take a chunk off a bin list. */staticvoidunlink_chunk(mstateav,mchunkptrp){if(chunksize(p)!=prev_size(next_chunk(p)))malloc_printerr("corrupted size vs. prev_size");mchunkptrfd=p->fd;mchunkptrbk=p->bk;if(__builtin_expect(fd->bk!=p||bk->fd!=p,0))malloc_printerr("corrupted double-linked list");fd->bk=bk;bk->fd=fd;if(!in_smallbin_range(chunksize_nomask(p))&&p->fd_nextsize!=NULL){if(p->fd_nextsize->bk_nextsize!=p||p->bk_nextsize->fd_nextsize!=p)malloc_printerr("corrupted double-linked list (not small)");if(fd->fd_nextsize==NULL){if(p->fd_nextsize==p)fd->fd_nextsize=fd->bk_nextsize=fd;else{fd->fd_nextsize=p->fd_nextsize;fd->bk_nextsize=p->bk_nextsize;p->fd_nextsize->bk_nextsize=fd;p->bk_nextsize->fd_nextsize=fd;}}else{p->fd_nextsize->bk_nextsize=p->bk_nextsize;p->bk_nextsize->fd_nextsize=p->fd_nextsize;}}}
/* Exhaust */if(remainder_size<MINSIZE){set_inuse_bit_at_offset(victim,size);if(av!=&main_arena)set_non_main_arena(victim);}/* Split */else{remainder=chunk_at_offset(victim,nb);/* We cannot assume the unsorted list is empty and therefore
have to perform a complete insert here. */bck=unsorted_chunks(av);fwd=bck->fd;if(__glibc_unlikely(fwd->bk!=bck))malloc_printerr("malloc(): corrupted unsorted chunks");remainder->bk=bck;remainder->fd=fwd;bck->fd=remainder;fwd->bk=remainder;if(!in_smallbin_range(remainder_size)){remainder->fd_nextsize=NULL;remainder->bk_nextsize=NULL;}set_head(victim,nb|PREV_INUSE|(av!=&main_arena?NON_MAIN_ARENA:0));set_head(remainder,remainder_size|PREV_INUSE);set_foot(remainder,remainder_size);}
在更大的 bin 中找:
注意
这一步不仅仅是 large request 的, small request 也会跑到这里来. 假设一个 small request 在对应的 smallbin 中没有, 但是更大的 smallbin (或者 largebin) 中有, 也会被取到.
/*
Search for a chunk by scanning bins, starting with next largest
bin. This search is strictly by best-fit; i.e., the smallest
(with ties going to approximately the least recently used) chunk
that fits is selected.
The bitmap avoids needing to check that most blocks are nonempty.
The particular case of skipping all bins during warm-up phases
when no chunks have been returned yet is faster than it might look.
*/++idx;bin=bin_at(av,idx);block=idx2block(idx);map=av->binmap[block];bit=idx2bit(idx);for(;;){/* Skip rest of block if there are no more set bits in this block. */if(bit>map||bit==0){do{if(++block>=BINMAPSIZE)/* out of bins */gotouse_top;}while((map=av->binmap[block])==0);bin=bin_at(av,(block<<BINMAPSHIFT));bit=1;}/* Advance to bin with set bit. There must be one. */while((bit&map)==0){bin=next_bin(bin);bit<<=1;assert(bit!=0);}/* Inspect the bin. It is likely to be non-empty */victim=last(bin);/* If a false alarm (empty bin), clear the bit. */if(victim==bin){av->binmap[block]=map&=~bit;/* Write through */bin=next_bin(bin);bit<<=1;}else{size=chunksize(victim);/* We know the first chunk in this bin is big enough to use. */assert((unsignedlong)(size)>=(unsignedlong)(nb));remainder_size=size-nb;/* unlink */unlink_chunk(av,victim);/* Exhaust */if(remainder_size<MINSIZE){set_inuse_bit_at_offset(victim,size);if(av!=&main_arena)set_non_main_arena(victim);}/* Split */else{remainder=chunk_at_offset(victim,nb);/* We cannot assume the unsorted list is empty and therefore
have to perform a complete insert here. */bck=unsorted_chunks(av);fwd=bck->fd;if(__glibc_unlikely(fwd->bk!=bck))malloc_printerr("malloc(): corrupted unsorted chunks 2");remainder->bk=bck;remainder->fd=fwd;bck->fd=remainder;fwd->bk=remainder;/* advertise as last remainder */if(in_smallbin_range(nb))av->last_remainder=remainder;if(!in_smallbin_range(remainder_size)){remainder->fd_nextsize=NULL;remainder->bk_nextsize=NULL;}set_head(victim,nb|PREV_INUSE|(av!=&main_arena?NON_MAIN_ARENA:0));set_head(remainder,remainder_size|PREV_INUSE);set_foot(remainder,remainder_size);}check_malloced_chunk(av,victim,nb);void*p=chunk2mem(victim);alloc_perturb(p,bytes);returnp;}}
ptmalloc2 用 4 个 32bit 的 block 来记录 bin 中是否最近有过 chunk. 如果某一位是 1, 则表示这一位对应的 bin 可能有 chunk. 这里用一位的近似 LRU 来实现.
因为在申请大小对应的 largebin 中并没有合适的 chunk, 所以往下一个 bin 中找, 也就是代码中的 ++idx. 然后找到对应的 block. 代码中的 block 是下标, 从 av 中取出然值后给 map. 找到 idx 对应的 bin 应该是 map 中的哪一位 bit. 这里的 bit 是 32 位数, 只有一位是 1.
if(bit>map||bit==0){do{if(++block>=BINMAPSIZE)/* out of bins */gotouse_top;}while((map=av->binmap[block])==0);bin=bin_at(av,(block<<BINMAPSHIFT));bit=1;}
找到这个 block 中, 最小满足条件的 bin:
1
2
3
4
5
6
7
/* Advance to bin with set bit. There must be one. */while((bit&map)==0){bin=next_bin(bin);bit<<=1;assert(bit!=0);}
根据 LRU, 找到的这个 bit 对应的 bin 中很可能有, 但是也可能没有. 如果没有, 则清除 map 对应的这个 bit, bit 往上一位, 重新循环找. 如果有, 则取出并返回. 取出和之前的几乎一模一样, 除了多设置了一个 last remainder, 不再赘述.
/* Inspect the bin. It is likely to be non-empty */victim=last(bin);/* If a false alarm (empty bin), clear the bit. */if(victim==bin){av->binmap[block]=map&=~bit;/* Write through */bin=next_bin(bin);bit<<=1;}else{remainder=chunk_at_offset(victim,nb);/* We cannot assume the unsorted list is empty and therefore
have to perform a complete insert here. */bck=unsorted_chunks(av);fwd=bck->fd;if(__glibc_unlikely(fwd->bk!=bck))malloc_printerr("malloc(): corrupted unsorted chunks 2");remainder->bk=bck;remainder->fd=fwd;bck->fd=remainder;fwd->bk=remainder;/* advertise as last remainder */if(in_smallbin_range(nb))av->last_remainder=remainder;if(!in_smallbin_range(remainder_size)){remainder->fd_nextsize=NULL;remainder->bk_nextsize=NULL;}set_head(victim,nb|PREV_INUSE|(av!=&main_arena?NON_MAIN_ARENA:0));set_head(remainder,remainder_size|PREV_INUSE);set_foot(remainder,remainder_size);}