5.7.2.3 分配large bin chunk(二)
如果通过上面的方式从最合适的
small bin
或
large bin
中都没有分配到需要的
chunk
,则查看比当前
bin
的
index
大的
small bin
或
large bin
是否有空闲
chunk
可利用来分配所需的
chunk
。源代码实现如下
/*
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);
获取下一个相邻
bin
的空闲
chunk
链表,并获取该
bin
对于
binmap
中的
bit
位的值。
Binmap
中的标识了相应的
bin
中是否有空闲
chunk
存在。
Binmap
按
block
管理,每个
block
为一个
int
,共
32
个
bit
,可以表示
32
个
bin
中是否有空闲
chunk
存在。使用
binmap
可以加快查找
bin
是否包含空闲
chunk
。这里只查询比所需
chunk
大的
bin
中是否有空闲
chunk
可用。
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 */
goto use_top;
} while ( (map = av->binmap[block]) == 0);
bin = bin_at(av, (block << BINMAPSHIFT));
bit = 1;
}
Idx2bit()
宏将
idx
指定的位设置为
1
,其它位清零,
map
表示一个
block
(
unsigned int
)值,如果
bit
大于
map
,意味着
map
为
0
,该
block
所对应的所有
bins
中都没有空闲
chunk
,于是遍历
binmap
的下一个
block
,直到找到一个不为
0
的
block
或者遍历完所有的
block
。退出循环遍历后,设置
bin
指向
block
的第一个
bit
对应的
bin
,并将
bit
置为
1
,表示该
block
中
bit 1
对应的
bin
,这个
bin
中如果有空闲
chunk
,该
chunk
的大小一定满足要求。
/* Advance to bin with set bit. There must be one. */
while ((bit & map) == 0) {
bin = next_bin(bin);
bit <<= 1;
assert(bit != 0);
}
在一个block遍历对应的bin,直到找到一个bit不为0退出遍历,则该bit对于的bin中有空闲chunk存在。
/* Inspect the bin. It is likely to be non-empty */
victim = last(bin);
将bin链表中的最后一个chunk赋值为victim。
/* 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;
如果victim与bin链表头指针相同,表示该bin中没有空闲chunk,binmap中的相应位设置不准确,将binmap的相应bit位清零,获取当前bin下一个bin,将bit移到下一个bit位,即乘以2。
}
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(victim, bck, fwd);
当前bin中的最后一个chunk满足要求,获取该chunk的大小,计算切分出所需chunk后剩余部分的大小,然后将victim从bin的链表中取出。
/* Exhaust */
if (remainder_size < MINSIZE) {
set_inuse_bit_at_offset(victim, size);
if (av != &main_arena)
victim->size |= NON_MAIN_ARENA;
如果剩余部分的大小小于
MINSIZE
,将整个
chunk
分配给应用层,设置
victim
的状态为
inuse
,如果当前分配区为非主分配区,设置
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 (__builtin_expect (fwd->bk != bck, 0))
{
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;
}
从victim
中切分出所需的
chunk
,剩余部分作为一个新的
chunk
加入到
unsorted
bin
中。如果剩余部分
chunk
属于
small bins
,将分配区的
last remainder chunk
设置为剩余部分构成的
chunk
;如果剩余部分
chunk
属于
large bins
,将剩余部分
chunk
的
chunk size
链表指针设置为
NULL
,因为
unsorted bin
中的
chunk
是不排序的,这两个指针无用,必须清零。
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);
设置victim和remainder的状态,由于remainder为空闲chunk,所以需要设置该chunk的foot。
}
check_malloced_chunk(av, victim, nb);
void *p = chunk2mem(victim);
if (__builtin_expect (perturb_byte, 0))
alloc_perturb (p, bytes);
return p;
调用chunk2mem()获得chunk中可用的内存指针,返回给应用层,退出。
}
}
如果从所有的
bins
中都没有获得所需的
chunk
,可能的情况为
bins
中没有空闲
chunk
,或者所需的
chunk
大小很大,下一步将尝试从
top
chunk
中分配所需
chunk
。源代码实现如下:
use_top:
/*
If large enough, split off the chunk bordering the end of memory
(held in av->top). Note that this is in accord with the best-fit
search rule. In effect, av->top is treated as larger (and thus
less well fitting) than any other available chunk since it can
be extended to be as large as necessary (up to system
limitations).
We require that av->top always exists (i.e., has size >=
MINSIZE) after initialization, so if it would otherwise be
exhausted by current request, it is replenished. (The main
reason for ensuring it exists is that we may need MINSIZE space
to put in fenceposts in sysmalloc.)
*/
victim = av->top;
size = chunksize(victim);
将当前分配区的top chunk赋值给victim,并获得victim的大小。
if ((unsigned long)(size) >= (unsigned long)(nb + MINSIZE)) {
remainder_size = size - nb;
remainder = chunk_at_offset(victim, nb);
av->top = remainder;
set_head(victim, nb | PREV_INUSE |
(av != &main_arena ? NON_MAIN_ARENA : 0));
set_head(remainder, remainder_size | PREV_INUSE);
check_malloced_chunk(av, victim, nb);
void *p = chunk2mem(victim);
if (__builtin_expect (perturb_byte, 0))
alloc_perturb (p, bytes);
return p;
}
由于top chunk
切分出所需
chunk
后,还需要
MINSIZE
的空间来作为
fencepost
,所需必须满足
top
chunk
的大小大于所需
chunk
的大小加上
MINSIZE
这个条件,才能从
top chunk
中分配所需
chunk
。从
top chunk
切分出所需
chunk
的处理过程跟前面的
chunk
切分类似,不同的是,原
top chunk
切分后的剩余部分将作为新的
top chunk
,原
top chunk
的
fencepost
仍然作为新的
top chunk
的
fencepost
,所以切分之后剩余的
chunk
不用
set_foot
。
#ifdef ATOMIC_FASTBINS
/* When we are using atomic ops to free fast chunks we can get
here for all block sizes. */
else if (have_fastchunks(av)) {
malloc_consolidate(av);
/* restore original bin index */
if (in_smallbin_range(nb))
idx = smallbin_index(nb);
else
idx = largebin_index(nb);
}
如果top chunk
也不能满足要求,查看
fast
bins
中是否有空闲
chunk
存在,由于开启了
ATOMIC_FASTBINS
优化情况下,
free
属于
fast bins
的
chunk
时不需要获得分配区的锁,所以在调用
_int_malloc()
函数时,有可能有其它线程已经向
fast bins
中加入了新的空闲
chunk
,也有可能是所需的
chunk
属于
small bins
,但通过前面的步骤都没有分配到所需的
chunk
,由于分配
small bin chunk
时在前面的步骤都不会调用
malloc_consolidate()
函数将
fast bins
中的
chunk
合并加入到
unsorted bin
中。所在这里如果
fast bin
中有
chunk
存在,调用
malloc_consolidate()
函数,并重新设置当前
bin
的
index
。并转到最外层的循环,尝试重新分配
small bin chunk
或是
large bin chunk
。如果开启了
ATOMIC_FASTBINS
优化,有可能在由其它线程加入到
fast bins
中的
chunk
被合并后加入
unsorted bin
中,从
unsorted bin
中就可以分配出所需的
large bin chunk
了,所以对没有成功分配的
large bin chunk
也需要重试。
#else
/*
If there is space available in fastbins, consolidate and retry,
to possibly avoid expanding memory. This can occur only if nb is
in smallbin range so we didn't consolidate upon entry.
*/
else if (have_fastchunks(av)) {
assert(in_smallbin_range(nb));
malloc_consolidate(av);
idx = smallbin_index(nb); /* restore original bin index */
}
如果top chunk
也不能满足要求,查看
fast bins
中是否有空闲
chunk
存在,如果
fast bins
中有空闲
chunk
存在,在没有开启
ATOMIC_FASTBINS
优化的情况下,只有一种可能,那就是所需的
chunk
属于
small bins
,但通过前面的步骤都没有分配到所需的
small bin chunk
,由于分配
small bin chunk
时在前面的步骤都不会调用
malloc_consolidate()
函数将
fast bins
中的空闲
chunk
合并加入到
unsorted bin
中。所在这里如果
fast bins
中有空闲
chunk
存在,调用
malloc_consolidate()
函数,并重新设置当前
bin
的
index
。并转到最外层的循环,尝试重新分配
small bin chunk
。
#endif
/*
Otherwise, relay to handle system-dependent cases
*/
else {
void *p = sYSMALLOc(nb, av);
if (p != NULL && __builtin_expect (perturb_byte, 0))
alloc_perturb (p, bytes);
return p;
山穷水尽了,只能想系统申请内存了。sYSMALLOc()函数可能分配的chunk包括small bin chunk,large bin chunk和large chunk。将在下一节中介绍该函数的实现。
}
}
至此,
_int_malloc()
函数的代码就罗列完了,当还有两个关键函数没有分析,一个为
malloc_consolidate()
,另一个为
sYSMALLOc()
,将在下面的章节介绍其实现。
分享到:
相关推荐
《Glibc内存管理--ptmalloc2源代码分析》 Glibc是GNU项目提供的C语言标准库,它在Linux系统中扮演着至关重要的角色。其中,内存管理是Glibc中的核心部分,它负责程序运行时的内存分配与释放,对系统的性能有着深远...
在分析glibc内存管理的ptmalloc源代码之前,我们需要先了解一些基础知识,包括操作系统对内存的分配和管理方法,以及glibc内存分配机制。 内存管理是操作系统的一个核心功能,它负责维护和管理计算机系统中的物理和...
《glibc内存管理ptmalloc源代码分析》是一份深入探讨Linux系统中glibc库内存管理机制的专业资料。glibc,全称GNU C Library,是Linux操作系统下广泛使用的C语言标准库,其中ptmalloc是glibc中负责动态内存分配的核心...
在深入探讨Glibc内存管理的Ptmalloc源代码之前,我们先来了解一下内存管理的基本概念。内存管理是操作系统和编程语言库中的核心组件,它负责有效地分配和回收内存,以确保程序的高效运行和资源的有效利用。 2.1 X86...
标题与描述概述的知识点主要集中在glibc内存管理的细节,特别是ptmalloc的源代码分析,这对于深入理解C库如何高效地处理内存分配和回收至关重要。以下是对这些知识点的详细解析: ### Glibc内存管理与ptmalloc ###...
glibc内存管理ptmalloc源代码分析
在深入探讨Glibc内存管理的Ptmalloc源代码之前,我们先来了解一下内存管理的基本概念和Glibc中的Ptmalloc2。内存管理是操作系统和应用程序中的核心部分,它负责为程序分配和释放内存,以确保资源的有效利用和避免...
### glibc内存管理ptmalloc源代码分析 #### 1. 问题 在开发一款NoSQL系统的过程中,我们遇到一个令人头疼的问题:系统中使用的内存管理模块,在高并发、高负载的场景下,虽然已将内存释放给C运行时库(即glibc),...
### glibc内存管理ptmalloc源代码分析-清晰版 #### 一、背景介绍与文档概览 本文档针对glibc中的ptmalloc2内存管理模块进行了深入的源代码分析,旨在帮助开发者更好地理解ptmalloc的工作原理及其内部机制。文档...
`glibc`是GNU项目提供的C标准库,而`ptmalloc`是它的一部分,负责程序的内存分配与管理。`ptmalloc`优化了内存分配效率,但在某些情况下也可能成为安全漏洞的来源。 `GDB`(GNU调试器)是一款强大的调试工具,它...
本文将深入探讨内存管理的基本原理及其调试方法,特别关注于Linux环境下的Glibc内存管理库以及Ptmalloc2的具体实现。 #### 二、基础知识 ##### 2.1 X86平台Linux进程内存布局 **2.1.1 32位模式下进程内存经典布局...
通过对glibc库中的ptmalloc源代码进行深入分析,我们不仅了解了其内部实现机制,还能够针对实际应用中的内存管理问题提出有效的解决方案。这对于提升程序性能、减少内存碎片以及提高系统的整体稳定性具有重要意义。...
学习自《glibc内存管理ptmalloc源代码分析》庄明强 著 部分资料参考自互联网 chunk 描述: 当用户通过malloc等函数申请空间时,实际上是从堆中分配内存 目前 Linux 标准发行版中使用的是 glibc 中的堆分配器:...
《堆漏洞的利用技巧》 堆溢出是计算机安全领域中的一个重要...同时,阅读glibc内存管理的源代码分析对于提高理解和实践能力非常有帮助。通过这些知识的学习,不仅可以增强安全意识,也能提升在CTF竞赛中的攻防能力。