malloc源码分析—_int_malloc
上一章分析了_int_malloc
的前面一小部分,本章继续往下看,
_int_malloc — fastbin
|
|
get_max_fast
返回fastbin可以存储内存的最大值,它在ptmalloc的初始化函数malloc_init_state
中定义,后面会分析这个函数。
如果需要分配的内存大小nb落在fastbin的范围内,首先调用fastbin_index
获得chunk大小nb
对应的fastbin索引。
|
|
减2是根据fastbin存储的内存最小值计算的,本章假设SIZE_SZ=4
,因此改写后idx = nb/8-2
。
获得索引idx后,就通过fastbin取出空闲chunk链表指针,mfastbinptr
其实就是malloc_chunk
指针,
|
|
下面的do、while循环又是一个CAS操作,其作用是从刚刚得到的空闲chunk链表指针中取出第一个空闲的chunk(victim),并将链表头设置为该空闲chunk的下一个chunk(victim->fd)。这里注意,fastbin中使用的是单链表,而后面smallbin使用的是双链表。
获得空闲chunk后,需要转换为可以存储的内存指针,chunk2mem
上一章分析过了,就是返回malloc_chunk
结构中fd所在的位置,因为当一个chunk被使用时,malloc_chunk
结构中fd
、bk
包括后面的变量都没有用了。最后调用alloc_perturb
对用户使用的内存进行初始化,然后就返回该内存的指针了。
假设fastbin中没有找到空闲chunk,或者fastbin根本没有初始化,或者其他原因,就进入下一步,从smallbin中获取内存,因此继续往下看.
_int_malloc — smallbin & largebin
|
|
首先
|
|
基于本章假设,MIN_LARGE_SIZE
经过换算后为512字节,因此低于512字节大小的内存块都归smallbin管理。
接下来通过bin_at
获得smallbin空闲chunk链表指针,
|
|
这里乘2,并且减去fd相对于malloc_chunk
中的位置是因为smallbin中存储的是fd和bk指针。last
定义为
|
|
该函数获得chunk的前一个chunk,由因为该chunk是smallbin的链表头,因此获得的是最后一个chunk,如果两者相等,表示对应的链表为空,什么都不做。
这里假设不相等,接下来有两种情况,第一种是victim=0
,表示smallbin还没有初始化,这里需要特别说明一下这里。smallbin初始化为malloc_chunk
指针数组,虽然定义为指针数组,但实际上存储的是fd和bk指针,如下所示
|fd|bk|fd|bk|…|fd|bk|
当smallbin还未初始化时,假设idx=1
,根据bin_at
取出的bin
是一个虚拟的malloc_chunk
指针,bin->fd
,是第二个fd,因此bin->bk
就是对应的bk,其值为0(bin->bk取出的不是地址,而是值)。因此当victim
为0时,可以断定smallbin未初始化,此时调用malloc_consolidate
进行初始化,
|
|
省略代码的if语句里是将fastbin中的chunk进行合并,然后添加到bins中,这里不分析,因为还未初始化,因此get_max_fast
返回0,后面的章节碰到了再分析。进入else部分,check_malloc_state
为空函数,malloc_init_state
就是主要的初始化函数,
|
|
该函数做了四件事情,第一是初始化malloc_state
中的bins
数组,初始化的结果是对bins
数组中的每一个fd
和对应的bk
,都初始化为fd
的地址,即fd=bk=&fd
;第二是设置fastbin可管理的内存块的最大值,即global_max_fast
,DEFAULT_MXFAST
定义为,
|
|
本章假设为64,set_max_fast
定义为
|
|
第三是设置一些标志位;第四是初始化分配去中的top chunk,就是一个malloc_chunk
指针,fd
保存在bins[0]
中(smallbin中不使用bins[0]
和bins[1]
)。
重新回到_int_malloc
中,假设victim
不为0,下面就从双向链表中取出victim
,设置其中的标志位,然后返回用户可分配的内存指针。
假设smallbin中没有空闲chunk可用,下面就要开始寻找largebin了,largebin_index
定义为
|
|
根据前面SIZE_SZ
的假设,这里largebin_index
对应的就是largebin_index_32
,定义为
|
|
这里就不多解释了,如果需要知道sz和索引的对应关系,可以自己计算一下。
再接下来have_fastchunks
根据标志位判断fastbin中是否有空闲chunk,如果有,就调用malloc_consolidate
将这些chunk和并,然后加入到unsortedbin中。
_int_malloc — 合并fastbin
下面重新看一下malloc_consolidate
函数。
|
|
因为ptmalloc前面已经初始化过了,这里直接进入if内部,首先通过clear_fastchunks
设置标志位表示fastbin中存在空闲chunk,
|
|
然后通过unsorted_chunks
获得bins数组中unsortedbin对应的malloc_chunk
指针(其fd
和bk
指针对应bins[0]
和bins[1]
)。
|
|
再往下,将fastbin中的最大和最小的chunk对应的malloc_chunk
指针赋值给maxfb
和fb
,然后通过do,while循环遍历fastbin中的每个chunk链表,atomic_exchange_acq
又是一个CAS操作,该函数取出fb
指针,并将原来的chunk链表头指针的值设为0,表示chunk链表空闲了。然后开始进入内层的循环,这里遍历的是每个chunk链表中的每个malloc_chunk
指针。
接下来首先去除chunk中的PREV_INUSE
和NON_MAIN_ARENA
标志,为了获得chunk的大小(size中的最低三位被用来作为标志位,并且fastbin中chunk的标志位IS_MMAPPED
默认为0)。然后通过chunk_at_offset
和chunksize
获得下一个chunk以及其大小,
|
|
再往下,如果chunk的前一个chunk没在使用中,就合并该chunk与前一个chunk,主要是重新计算malloc_chunk
的指针,并调用unlink
将前一个chunk从bins数组中删除,
|
|
简单来说,该宏定义就是将前一个chunk从两个双线链表中删除,fd
和bk
指针构成的双向链表存在于smallbin和largebin中,fd_nextsize
和bk_nextsize
指针构成的双向链表只存在于largebin中。
再往下,如果相邻的下一个chunk不是top chunk,并且下一个chunk不在使用中,就继续合并,否则,就清除下一个chunk的PREV_INUSE
,表示该chunk已经空闲了。
然后将刚刚合并完的chunk添加进unsorted_bin
中,unsorted_bin
也是一个双向链表。
如果合并完的chunk属于smallbin的大小,则需要清除fd_nextsize
和bk_nextsize
,因为smallbin中的chunk不会使用这两个指针。并且通过setHead
保证不会有相邻的两个chunk都空闲,并且通过setFoot
设置下一个chunk的prev_size
。
如果相邻的下一个chunk是top chunk,则将合并完的chunk继续合并到top chunk中。
至此,malloc_consolidate
就分析完了,总结一下,malloc_consolidate
就是遍历fastbin中每个chunk链表的每个malloc_chunk
指针,合并前一个不在使用中的chunk,如果后一个chunk是top chunk,则直接合并到top chunk中,如果后一个chunk不是top chunk,则合并后一个chunk并添加进unsorted_bin
中。
下一章继续往下分析_int_malloc函数。