目录
一、_int_malloc内存分配的核心函数
二、bins的管理和chunk的结构
三、_int_malloc函数分配前的两个检查
四、REMOVE_FB原子实现fastbins的链表操作
五、fastbins的具体分配实现
一、_int_malloc内存分配的核心函数
前几章节,我们主要讲解了状态机malloc_state、内存组织单元malloc_chunk、分配区Arena三个概念。此处有必要再次再次加强这几个概念的理解:
- 状态机:是指malloc_state这个数据结构,该结构用来管理分配区下面的内存组织结构,例如:fastbins、smallbins、largebins等。通过状态机,我们就能找到对应的内存应该在那个bins上分配
- 内存组织单元:是指malloc_chunk这个数据结构,该结构主要管理每一次分配的内存,chunk分为数据记录区和数据区,通过chunk我们也能将不同的chunk进行双向链表放置到不同的bins上
- 分配区:分配区分为主分配区和非主分配区,主要为了避免多线程争抢。
内存是如何分配的,以及如何将分配的内存关联到malloc_chunk,然后放置到malloc_state的状态机上,则是由_int_malloc函数实现的。
我们继续回到入口函数__libc_malloc :
- 首选通过arena_get函数,获取一个分配区,得到ar_ptr的值(也是当前分配区状态机malloc_state)
- 然后调用_int_malloc函数,传入ar_ptr和bytes参数,最终得到victim值,即malloc_chunk这个结构
- 如果victim的值为NULL,分配失败,则调用arean_get_retry重新获取一个新的分配区,并重新执行_int_malloc函数
/* 获取一个分配区,分配区分为主分配区和线程分配区 ,并加锁*/
arena_get (ar_ptr, bytes);
/* 执行malloc的分配操作 */
victim = _int_malloc (ar_ptr, bytes);
/* Retry with another arena only if we were able to find a usable arena
before. 如果执行分配失败,则再执行一遍 */
if (!victim && ar_ptr != NULL)
{
LIBC_PROBE (memory_malloc_retry, 1, bytes);
ar_ptr = arena_get_retry (ar_ptr, bytes); //重新获取分配区
victim = _int_malloc (ar_ptr, bytes);
}
/* 解除锁;arena_get函数里面,如果分配区能够取到,则会上锁 */
if (ar_ptr != NULL)
__libc_lock_unlock (ar_ptr->mutex);
assert (!victim || chunk_is_mmapped (mem2chunk (victim)) ||
ar_ptr == arena_for_chunk (mem2chunk (victim)));
return victim;
}
二、bins的管理和chunk的结构
这一节还是要继续回顾一下bins的管理方式和chunk的结构。这个比较重要,后续会贯穿整个_int_malloc的实现。
bins的实现:
在malloc_state状态机里面,有两个结构对象:fastbinsY和bins
fastbinsY:fast bins是bins的高速缓冲区,大约有10个定长队列。当用户释放一块不大于max_fast(默认值64)的chunk(一般小内存)的时候,会默认会被放到fast bins上。
bins:主要管理small bins、large bins、unstored bins,在一个数组上,通过数组下标和步长方式来区分不同类型的bins
chunk实现:
malloc_chunk结构主要管理了chunk的大小,双向链表的指针以及空闲链表的指针等。通过chunk结构也能获取到实际存储的数据区域。
三、_int_malloc函数分配前的两个检查
_int_malloc入参mstate av和size_t bytes,即分配区状态机结构和内存分配的大小。
分配前需要做两个检查:
-
检查bytes是否在合法区间内,最大小于<2147483647
-
如果分配区结构为空,则调用sysmalloc进行再次分配
/**
* malloc的核心分配函数
* av:分配区
* bytes:内存的大小
*/
static void *
_int_malloc (mstate av, size_t bytes)
{
........
//检查bytes是否在合法区间内,最大小于<2147483647
if (!checked_request2size (bytes, &nb))
{
__set_errno (ENOMEM);
return NULL;
}
/* There are no usable arenas. Fall back to sysmalloc to get a chunk from
mmap. */
/* 如果分配区结构为空,则调用sysmalloc进行再次分配 */
if (__glibc_unlikely (av == NULL))
{
void *p = sysmalloc (nb, av);
if (p != NULL)
alloc_perturb (p, bytes);
return p;
}
四、REMOVE_FB原子实现fastbins的链表操作
定义了一个REMOVE_FB宏函数,该函数主要目的: 原子操作,从fast bins链表上,弹出一个chunk
因为fastbinsY是一个数组,数组上挂载malloc_chunk是双向链表,当多线程进行同时操作链表的时候就会冲突,需要进行原子操作
核心看该函数:define catomic_compare_and_exchange_val_acq(mem, newval, oldval),mem指向newval,并且返回oldval的值
/*
* 该函数就是原子操作,从fast bins链表上,弹出一个chunk
*
* 核心看该函数:
* define catomic_compare_and_exchange_val_acq(mem, newval, oldval)
* mem指向newval,并且返回oldval的值
* */
#define REMOVE_FB(fb, victim, pp) \
do \
{ \
victim = pp; \
if (victim == NULL) \
break; \
} \
while ((pp = catomic_compare_and_exchange_val_acq (fb, victim->fd, victim)) \
!= victim);
五、fastbins的具体分配实现
fastbins放置在malloc_state-> fastbinsY数组上,是高速缓冲区(主要用于小内存的快速分配和释放),大约有10个定长队列。
当用户释放一块不大于max_fast(默认值64)的chunk(一般小内存)的时候,会默认会被放到fast bins上。
_int_malloc函数中,先看申请的内存大小nb是否符合fast bins的限制,符合的话,首先进入fast bin的分配。
if ((unsigned long) (nb) <= (unsigned long) (get_max_fast ()))
{
idx = fastbin_index (nb); //找到fastbin的数组下标idx
mfastbinptr *fb = &fastbin (av, idx); //av->fastbinsY[idx]
mchunkptr pp;
victim = *fb; //vitctim是malloc_chunk结构
/* 如果在fastbin上能够找到空闲的小于64b的chunk,则使用该chunk;
* 如果在fastbin上没有合适的chunk,则去下面的small bins上去创建一个chunk */
if (victim != NULL)
{
if (SINGLE_THREAD_P)
*fb = victim->fd; //单线程
else
REMOVE_FB (fb, pp, victim); //设置 原子操作,将chunk从fast bins上摘下来,多线程的时候会争抢
/* REMOVE_FB是原子操作,所以这里需要继续判断一下,是否摘成功了,victim是否有值*/
if (__glibc_likely (victim != NULL))
{
size_t victim_idx = fastbin_index (chunksize (victim));
if (__builtin_expect (victim_idx != idx, 0))
malloc_printerr ("malloc(): memory corruption (fast)");
check_remalloced_chunk (av, victim, nb);
..........................简.............
void *p = chunk2mem (victim); //通过chunk结构,获取data数据部分的指针地址
alloc_perturb (p, bytes); //初始化内存块
return p;
}
}
}
下一章节,我们继续看_int_malloc函数里面的smallbins、largebins实现