从一个缓存中分配对象总是遵循下面的原则:
1.本地高速缓存中是否有空闲对象,如果有的话则从其中获取对象,
2.如果本地高速缓存中没有对象,则从kmem_list3中的slab链表中寻找空闲对象并填充到本地高速缓存再分配;
3.如果所有的slab中都没有空闲对象了,那么就要创建新的slab,再分配 。
来自:http://blog.csdn.net/vanbreaker/article/details/7671211
Linux内核从slab中分配内存空间由kmalloc()或kmem_cache_alloc()函数实现。
kmalloc()->__kmalloc()->__do_kmalloc();
/**
* __do_kmalloc - allocate memory
* @size: how many bytes of memory are required.
* @flags: the type of memory to allocate (see kmalloc).
* @caller: function caller for debug tracking of the caller
*/
static __always_inline void *__do_kmalloc(size_t size, gfp_t flags,
void *caller)
{
struct kmem_cache *cachep;
void *ret;
/* If you want to save a few bytes .text space: replace
* __ with kmem_.
* Then kmalloc uses the uninlined functions instead of the inline
* functions.
*/ /*查找指定大小的通用cache,关于sizes数组,在前面
的初始化中就已经分析过了*/
cachep = __find_general_cachep(size, flags);
if (unlikely(ZERO_OR_NULL_PTR(cachep)))
return cachep;
ret = __cache_alloc(cachep, flags, caller); /*实际的分配工作*/
trace_kmalloc((unsigned long) caller, ret,
size, cachep->size, flags);
return ret;
}
最后调用 实际的分配工作: __do_cache_alloc()->__cache_alloc()->____cache_alloc();
static inline void *____cache_alloc(struct kmem_cache *cachep, gfp_t flags)
{
void *objp;
struct array_cache *ac;
bool force_refill = false;
check_irq_off();
ac = cpu_cache_get(cachep); /* 获得本CPU的local cache */
if (likely(ac->avail)) {/* 如果local cache中有可用的空闲对象 */
ac->touched = 1;
<span style="white-space:pre"> /* 从local cache的entry数组中提取最后面的空闲对象 */ </span>
objp = ac_get_obj(cachep, ac, flags, false);
/*
* Allow for the possibility all avail objects are not allowed
* by the current flags
*/
if (objp) {
STATS_INC_ALLOCHIT(cachep);
goto out;
}
force_refill = true; //标志位 是否需要refill
}
STATS_INC_ALLOCMISS(cachep); /* 从slab三链中提取空闲对象填充到local cache中 */
objp = cache_alloc_refill(cachep, flags, force_refill);
/*
* the 'ac' may be updated by cache_alloc_refill(),
* and kmemleak_erase() requires its correct value.
*/ /* cache_alloc_refill的cache_grow打开了中断,local cache指针可能发生了变化,需要重新获得
ac = cpu_cache_get(cachep);
out:
/*
* To avoid a false negative, if an object that is in one of the
* per-CPU caches is leaked, we need to make sure kmemleak doesn't
* treat the array pointers as a reference to the object.
*/
if (objp)
kmemleak_erase(&ac->entry[ac->avail]);/* 分配出去的对象,其entry指针指向空 */
return objp;
}
static inline void *ac_get_obj(struct kmem_cache *cachep,
struct array_cache *ac, gfp_t flags, bool force_refill)
{
void *objp;
if (unlikely(sk_memalloc_socks()))
objp = __ac_get_obj(cachep, ac, flags, force_refill);
else
<span style="white-space:pre"> /*先将avail的值减1,这样avail对应的空闲对象是最热的,即最近释放出来的,
更有可能驻留在CPU高速缓存中*/ </span>
objp = ac->entry[--ac->avail];
return objp;
}
tatic void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags,
bool force_refill)
{
int batchcount;
struct kmem_list3 *l3;
struct array_cache *ac;
int node;
check_irq_off();
node = numa_mem_id(); /* 获得本内存节点,UMA只有一个节点 */
if (unlikely(force_refill))
goto force_grow;
retry:
ac = cpu_cache_get(cachep);
batchcount = ac->batchcount; /*获取批量转移的数目*/
if (!ac->touched && batchcount > BATCHREFILL_LIMIT) {
/*
* If there was little recent activity on this cache, then
* perform only a partial refill. Otherwise we could generate
* refill bouncing.
*/ /* 最近未使用过此local cache,没有必要添加过多的对象
,添加的数目为默认的限定值 */
batchcount = BATCHREFILL_LIMIT;
}
l3 = cachep->nodelists[node];/*获取kmem_list3*/
BUG_ON(ac->avail > 0 || !l3);
spin_lock(&l3->list_lock);
/* See if we can refill from the shared array */
<span style="white-space:pre"> /* shared local cache用于多核系统中,为所有cpu共享
,如果有共享本地高速缓存
,那么首先从shared local cache中批量搬运空闲对象到local cache中
。通过shared local cache使填充工作变得简单。*/ </span>
if (l3->shared && transfer_objects(ac, l3->shared, batchcount)) {
l3->shared->touched = 1;
goto alloc_done;
}
<span style="white-space:pre"> /* 如果没有shared local cache,或是其中没有空闲的对象
,从slab链表中分配 */ </span>
while (batchcount > 0) {
struct list_head *entry;
struct slab *slabp;
/* Get slab alloc is to come from. */
<span style="white-space:pre"> /*扫描slab链表,先从partial链表开始,如果整个partial链表都无法找到batchcount个空闲对象,
</span> <span style="white-space:pre"> 再扫描free链表*/ </span>
entry = l3->slabs_partial.next;
if (entry == &l3->slabs_partial) { /*entry回到表头说明partial链表已经扫描完毕,开始扫描free链表*/
l3->free_touched = 1; /* 表示刚刚访问了slab空链表 */
entry = l3->slabs_free.next;
if (entry == &l3->slabs_free)/* 空slab链表也为空,必须增加slab了 */
goto must_grow;
}
slabp = list_entry(entry, struct slab, list);
check_slabp(cachep, slabp);
check_spinlock_acquired(cachep);
/*
* The slab was either on partial or free list so
* there must be at least one object available for
* allocation.
*/
BUG_ON(slabp->inuse >= cachep->num);
<span style="white-space:pre"> /*如果slabp中还存在空闲对象并且还需要继续填充对象到本地高速缓存*/ </span>
while (slabp->inuse < cachep->num && batchcount--) {
STATS_INC_ALLOCED(cachep);
STATS_INC_ACTIVE(cachep);
STATS_SET_HIGH(cachep);
<span style="white-space:pre"> /*填充的本质就是用ac后面的void*数组元素指向一个空闲对象</span>
<span style="white-space:pre"> ac->entry[ac->avail++] = slab_get_obj(cachep, slabp, node); </span>
<span style="white-space:pre"> */ </span>
ac_put_obj(cachep, ac, slab_get_obj(cachep, slabp,node));
}
check_slabp(cachep, slabp);
/* move slabp to correct slabp list: */
list_del(&slabp->list);
if (slabp->free == BUFCTL_END) /*free等于BUFCTL_END表示空闲对象已耗尽,将slab插入full链表*/
list_add(&slabp->list, &l3->slabs_full);
else
list_add(&slabp->list, &l3->slabs_partial);
}
must_grow:
<span style="white-space:pre"> /* 前面从slab链表中添加avail个空闲对象到local cache中
,更新slab链表的空闲对象数 */ </span>
l3->free_objects -= ac->avail;
alloc_done:
spin_unlock(&l3->list_lock);
if (unlikely(!ac->avail)) { /* local cache中仍没有可用的空闲对象,说明slab
三链中也没有空闲对象,需要创建新的空slab了 */
int x;
force_grow:
x = cache_grow(cachep, flags | GFP_THISNODE, node, NULL); /* 创建一个空slab */
/* cache_grow can reenable interrupts, then ac could change. */
ac = cpu_cache_get(cachep);
node = numa_mem_id();
/* no objects in sight? abort */
if (!x && (ac->avail == 0 || force_refill))
return NULL;
if (!ac->avail) /* objects refilled by interrupt? */
goto retry;
}
ac->touched = 1;
return ac_get_obj(cachep, ac, flags, force_refill); /* 返回local cache中最后一个空闲对象的虚拟地址 objp = ac->entry[--ac->avail];*/
}
辅助函数:
/*
* Transfer objects in one arraycache to another.
* Locking must be handled by the caller.
*
* Return the number of entries transferred.
*/
static int transfer_objects(struct array_cache *to,
struct array_cache *from, unsigned int max)
{
/* Figure out how many entries to transfer */
int nr = min(min(from->avail, max), to->limit - to->avail);
if (!nr)
return 0;
/*拷贝*/
memcpy(to->entry + to->avail, from->entry + from->avail -nr,
sizeof(void *) *nr);
/*两边数据更新*/
from->avail -= nr;
to->avail += nr;
to->touched = 1;
return nr;
}
/*从slab中提取一个空闲对象*/
static void *slab_get_obj(struct kmem_cache *cachep, struct slab *slabp,
int nodeid)
{
/* 获得一个空闲的对象,free是本slab中第一个空闲对象的索引 */
void *objp = index_to_obj(cachep, slabp, slabp->free);
kmem_bufctl_t next;
/* 更新在用对象计数 */
slabp->inuse++;
/* 获得下一个空闲对象的索引 */
next = slab_bufctl(slabp)[slabp->free];
#if DEBUG
slab_bufctl(slabp)[slabp->free] = BUFCTL_FREE;
WARN_ON(slabp->nodeid != nodeid);
#endif
/* free指向下一个空闲的对象 */
slabp->free = next;
return objp;
}
static inline void *index_to_obj(struct kmem_cache *cache, struct slab *slab,
unsigned int idx)
{ /* s_mem是slab中第一个对象的起始地址,buffer_size是每个对象的大小
,这里根据对象索引计算对象的地址 */
return slab->s_mem + cache->buffer_size * idx;
}
static inline kmem_bufctl_t *slab_bufctl(struct slab *slabp)
{
return (kmem_bufctl_t *) (slabp + 1);
}
static inline struct array_cache *cpu_cache_get(struct kmem_cache *cachep)
{
return cachep->array[smp_processor_id()];
}
对于cache_grow 扩容 以后分析;