文章目录
_int_malloc流程分析
1.尝试在fastbin里面寻找
若needbytes 是小于fastbin最大chunk的大小,根据needbytes计算出idx,在对应的bin里面看一下有没有chunk。若对应的bin并不是空的,那么取出一个chunk 名为victim,检查一下victim的size是否与所在的bin对应,然后返回。否则往下执行
对应代码:
if ((unsigned long)(nb) <= (unsigned long)(get_max_fast()))
{
idx = fastbin_index(nb);
mfastbinptr *fb = &fastbin(av, idx);
mchunkptr pp = *fb;
do
{
victim = pp;
if (victim == NULL)
break;
} while ((pp = catomic_compare_and_exchange_val_acq(fb, victim->fd, victim)) != victim);
//检测目标chunk的size是否正确.
if (victim != 0)
{
if (__builtin_expect(fastbin_index(chunksize(victim)) != idx, 0))
{
errstr = "malloc(): memory corruption (fast)";
errout:
malloc_printerr(check_action, errstr, chunk2mem(victim), av);
return NULL;
}
check_remalloced_chunk(av, victim, nb);
void *p = chunk2mem(victim);
alloc_perturb(p, bytes);
return p;
}
}
2.尝试在small bin里面寻找
若needbytes是属于small bin范围,那么根据needbytes计算出idx,看一下对应的bin里面是否有chunk,若有的话,取出最后一个chunk,返回。否则往下执行。
对应代码:
if (in_smallbin_range(nb))
{
//0x20,0x30.......0x410
idx = smallbin_index(nb);
bin = bin_at(av, idx);
if ((victim = last(bin)) != bin)
{
//一开始每一个Bin里面的地址都是0,得进行初始化.
if (victim == 0) /* initialization check */
malloc_consolidate(av);
else
{
bck = victim->bk;
if (__glibc_unlikely(bck->fd != victim))
{
errstr = "malloc(): smallbin double linked list corrupted";
goto errout;
}
set_inuse_bit_at_offset(victim, nb);
bin->bk = bck;
bck->fd = bin;
if (av != &main_arena)
victim->size |= NON_MAIN_ARENA;
check_malloced_chunk(av, victim, nb);
void *p = chunk2mem(victim);
alloc_perturb(p, bytes);
return p;
}
}
}
3.触发malloc_consolidate
若needbytes不属于small bin,那么会触发malloc_consolidate来合并fastbin里面的chunk。
由于fastbin里面的chunk始终被认为是inuse,所以fastbin里面的chunk除了malloc_consolidate函数能够将其合并之外,没有其他的机会。合并的原因就是(可能)提供所需的large chunk。
对应代码:
else
{
idx = largebin_index(nb);
//由于fastbin里面的chunk始终是被认为使用的,所以不会被合并,
//但是现在需要large 的chunk,得合并fastbin里面的chunk,可能提供large chunk
if (have_fastchunks(av))
malloc_consolidate(av);
}
4.for()循环:
- 从后往前遍历unsorted bin里面的chunk:
-
- 若unsorted bin里面只有一个chunk,满足need bytes 是small bin范围内,这个chunk 是reminder,reminder除去needbytes之任能形成一个chunk,那么就从这个chunk上分割出需要的大小,剩余部分仍然保留在unsorted bin里面,reminder指向剩余的部分
if (in_smallbin_range(nb) &&bck == unsorted_chunks(av) &&victim == av->last_remainder &&
(unsigned long)(size) > (unsigned long)(nb + MINSIZE))
{
/* split and reattach remainder */
remainder_size = size - nb;
remainder = chunk_at_offset(victim, nb);
//剩余的部分继续放到unsorted bin里面
unsorted_chunks(av)->bk = unsorted_chunks(av)->fd = remainder;
av->last_remainder = remainder;
remainder->bk = remainder->fd = unsorted_chunks(av);
//
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);
return p;
}
-
- 将chunk从unsorted bin里面摘除
unsorted_chunks(av)->bk = bck;
bck->fd = unsorted_chunks(av);
-
- 若chunk size 和need bytes相等,返回这个chunk
if (size == nb)
{
set_inuse_bit_at_offset(victim, size);
if (av != &main_arena)
victim->size |= NON_MAIN_ARENA;
check_malloced_chunk(av, victim, nb);
void *p = chunk2mem(victim);
alloc_perturb(p, bytes);
return p;
}
-
- 将该chunk 放到对应的normal bin里面,并标记bitmap
if (in_smallbin_range(size))
{
victim_index = smallbin_index(size);
//bck 指向的是数组
//fwd 是next
bck = bin_at(av, victim_index);
fwd = bck->fd;
}
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((bck->bk->size & NON_MAIN_ARENA) == 0);
//新的chunk比最小的chun大小还要小,就把新的这个chunk放在large bin的末尾.
if ((unsigned long)(size) < (unsigned long)(bck->bk->size))
{
fwd = bck;
bck = bck->bk;
//设置victim 的fd_nextsize和bk_nextsize
victim->fd_nextsize = fwd->fd;
victim->bk_nextsize = fwd->fd->bk_nextsize;
//设置victim 下一个chunk (head->fd)的bk__nextsize和前一个chunk的fd_nextsize;
fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
}
else
{
//否则找到合适的位置.
assert((fwd->size & NON_MAIN_ARENA) == 0);
while ((unsigned long)size < fwd->size)
{
fwd = fwd->fd_nextsize;
assert((fwd->size & NON_MAIN_ARENA) == 0);
}
//victim 和fwd 一样大
if ((unsigned long)size == (unsigned long)fwd->size)
/* Always insert in the second position. */
fwd = fwd->fd;
else
{
//victim 比fwd 更大.
victim->fd_nextsize = fwd;
victim->bk_nextsize = fwd->bk_nextsize;
//
//后一个nextsize chunk
fwd->bk_nextsize = victim;
//前一个nextsize chunk
victim->bk_nextsize->fd_nextsize = victim;
}
bck = fwd->bk;
}
}
else
//对应的large bin是空的
victim->fd_nextsize = victim->bk_nextsize = victim;
}
//在bitmap里面标记这个bin不是空的.
mark_bin(av, victim_index);
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;
- 若need bytes不属于small bin范围,即在large chunk范围内:
-
- 找到对应bin
-
- 若对应的bin不为空,而且在这个bin里面最小的chunk(也就是最后一个chunk) 的chunk size >= needbytes,从后往前遍历,寻找到第一个>= needbytes的chunk,然后unlink,在这个操作中会避免取下每种size的第一个chunk(因为还得调整bk_nextsize和fd_nextsize)。取下之后在这个chunk上分割出需要的大小,并把剩余的部分插到unsorted bin的开头,返回
bin = bin_at(av, idx);
/* skip scan if empty or largest chunk is too small */
if ((victim = first(bin)) != bin &&
(unsigned long)(victim->size) >= (unsigned long)(nb))
{
victim = victim->bk_nextsize;
//从后往前遍历..寻找第一个 >= nb的chunk
while (((unsigned long)(size = chunksize(victim)) <(unsigned long)(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) && victim->size == victim->fd->size)
victim = victim->fd;
remainder_size = size - nb;
unlink(av, victim, bck, fwd);
/* Exhaust */
if (remainder_size < MINSIZE)
{
set_inuse_bit_at_offset(victim, size);
if (av != &main_arena)
victim->size |= NON_MAIN_ARENA;
}
/* Split */
else
{
//把reminder 插入到unsorted bin的开头位置
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))
{
errstr = "malloc(): corrupted unsorted chunks";
goto errout;
}
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);
return p;
}
- 扫描bins找到能提供needbytes的chunk
开头有这样一段代码:
++idx;
bin = bin_at(av, idx);
block = idx2block(idx);
map = av->binmap[block];
bit = idx2bit(idx);
如果能执行到这里,说明前面的代码在idx对应的bin里面没有找到能提供needbytes的chunk,这里idx++,接下来就从下一个bin开始寻找。
(这里是通过bitmap来遍历bin,没啥复杂的,详细的看代码就行)
for():
-
- 从下一个bin开始,通过bitmap找到能提供need bytes的bin,若没有找到,执行use_top
if (bit > map || bit == 0)
{
//先找到能满足需求的 binmap,
do
{
if (++block >= BINMAPSIZE) /* out of bins */
goto use_top;
} while ((map = av->binmap[block]) == 0);
//bin对应第一个
bin = bin_at(av, (block << BINMAPSHIFT));
bit = 1;
}
//执行到这里就一定有更大的chunk,
//检查当前的block里面是否有大的bin
/* Advance to bin with set bit. There must be one. */
while ((bit & map) == 0)
{
bin = next_bin(bin);
bit <<= 1;
assert(bit != 0);
}
-
- 若bin为空,进行下一次循环
/* 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;
}
-
- 不为空的话,就在最后一个chunk (large bin里面这个是最小的chunk,但是足够了)划分出需要的内存,剩余的部分做出相应的处理
else
{
size = chunksize(victim);
/* We know the first chunk in this bin is big enough to use. */
assert((unsigned long)(size) >= (unsigned long)(nb));
remainder_size = size - nb;
/* unlink */
unlink(av, victim, bck, fwd);
/* Exhaust */
if (remainder_size < MINSIZE)
{
set_inuse_bit_at_offset(victim, size);
if (av != &main_arena)
victim->size |= NON_MAIN_ARENA;
}
/* 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))
{
errstr = "malloc(): corrupted unsorted chunks 2";
goto errout;
}
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);
return p;
}
5 use top
- 若top chunk能提供所需的大小,从top chunk上分割出需要的内存,返回。
- 否则 若有fastbin的话,那么合并fastbin里面的chunk
- 若没有fastbin,通过sysmalloc 分配内存,返回
其他部分:
1.关于last_reminder
/* The remainder from the most recent split of a small request */
mchunkptr last_remainder;
这个指针指向的是“最近的某个 small request划分剩下的chunk”,然后大家应该就知道了前面的_int_malloc中什么时候划分之后需要把last_reminder指向剩下的chunk。