全部博文(50)
分类: LINUX
2014-04-04 11:55:06
原文地址:Vi Linux内存 之 Slab分配器(三) 作者:palals
从slab系统中获得一个空闲的对象。调用关系为:kmem_cache_alloc->__cache_alloc-> __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;
check_irq_off();
/* 获得本CPU的local cache */
ac = cpu_cache_get(cachep);
/* 如果local cache中有可用的空闲对象 */
if (likely(ac->avail)) {
/* 更新local cache命中计数 */
STATS_INC_ALLOCHIT(cachep);
/* touched置1表示最近使用了local cache,这会影响填充local cache时的数目,最近使用的填充较多的对象 */
ac->touched = 1;
/* 从local cache的entry数组中提取最后面的空闲对象 */
objp = ac->entry[--ac->avail];
} else {
/* local cache中没有空闲对象,更新未命中计数 */
STATS_INC_ALLOCMISS(cachep);
/* 从slab三链中提取空闲对象填充到local cache中 */
objp = cache_alloc_refill(cachep, flags);
/*
* 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);
}
/*
* 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.
*/
/* 分配出去的对象,其entry指针指向空 */
if (objp)
kmemleak_erase(&ac->entry[ac->avail]);
return objp;
}
cache_alloc_refill
从slab三链中提取一部分空闲对象填充到local cache中。
static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags)
{
int batchcount;
struct kmem_list3 *l3;
struct array_cache *ac;
int node;
retry:
check_irq_off();
/* 获得本内存节点,UMA只有一个节点 */
node = numa_mem_id();
/* 获得本CPU的local cache */
ac = cpu_cache_get(cachep);
/* 批量填充的数目,local cache是按批填充的 */
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;
}
/* 获得本内存节点、本cache的slab三链 */
l3 = cachep->nodelists[node];
BUG_ON(ac->avail > 0 || !l3);
spin_lock(&l3->list_lock);
/* See if we can refill from the shared array */
/* shared local cache用于多核系统中,为所有cpu共享,如果slab cache包含一个这样的结构,那么首先从shared local cache中批量搬运空闲对象到local cache中。通过shared local cache使填充工作变得简单。*/
if (l3->shared && transfer_objects(ac, l3->shared, batchcount)) {
l3->shared->touched = 1;
goto alloc_done;
}
/* 如果没有shared local cache,或是其中没有空闲的对象,从slab链表中分配 */
while (batchcount > 0) {
struct list_head *entry;
struct slab *slabp;
/* Get slab alloc is to come from. */
/* 先从部分满slab链表中分配 */
entry = l3->slabs_partial.next;
/* next指向头节点本身,说明部分满slab链表为空 */
if (entry == &l3->slabs_partial) {
/* 表示刚刚访问了slab空链表 */
l3->free_touched = 1;
/* 检查空slab链表 */
entry = l3->slabs_free.next;
/* 空slab链表也为空,必须增加slab了 */
if (entry == &l3->slabs_free)
goto must_grow;
}
/* 获得链表节点所在的slab */
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);
while (slabp->inuse < cachep->num && batchcount--) {
/* 更新调试用的计数器 */
STATS_INC_ALLOCED(cachep);
STATS_INC_ACTIVE(cachep);
STATS_SET_HIGH(cachep);
/* 从slab中提取一个空闲对象,将其虚拟地址插入到local cache中 */
ac->entry[ac->avail++] = slab_get_obj(cachep, slabp,
node);
}
check_slabp(cachep, slabp);
/* move slabp to correct slabp list: */
/* 从原链表中删除此slab节点,list表示此slab位于哪个链表(满、部分满、空)中 */
list_del(&slabp->list);
if (slabp->free == BUFCTL_END)
/* 此slab中已经没有空闲对象,添加到“full”slab链表中 */
list_add(&slabp->list, &l3->slabs_full);
else
/* 还有空闲对象,添加到“partial”slab链表中 */
list_add(&slabp->list, &l3->slabs_partial);
}
must_grow:
/* 前面从slab链表中添加avail个空闲对象到local cache中,更新slab链表的空闲对象数 */
l3->free_objects -= ac->avail;
alloc_done:
spin_unlock(&l3->list_lock);
/* local cache中仍没有可用的空闲对象,说明slab三链中也没有空闲对象,需要创建新的空slab了 */
if (unlikely(!ac->avail)) {
int x;
/* 创建一个空slab */
x = cache_grow(cachep, flags | GFP_THISNODE, node, NULL);
/* cache_grow can reenable interrupts, then ac could change. */
/* 上面的操作使能了中断,此期间local cache指针可能发生了变化,需要重新获得 */
ac = cpu_cache_get(cachep);
/* 无法新增空slab,local cache中也没有空闲对象,表明系统已经无法分配新的空闲对象了 */
if (!x && ac->avail == 0) /* no objects in sight? abort */
return NULL;
/* 走到这有两种可能,第一种是无论新增空slab成功或失败,只要avail不为0,表明是其他进程重填了local cache,本进程就不需要重填了,不执行retry流程。第二种是avail为0,并且新增空slab成功,则进入retry流程,利用新分配的空slab填充local cache */
if (!ac->avail) /* objects refilled by interrupt? */
goto retry;
}
/* 重填了local cache,设置近期访问标志 */
ac->touched = 1;
/* 返回local cache中最后一个空闲对象的虚拟地址 */
return ac->entry[--ac->avail];
}
slab_get_obj
从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;
}