`
tomhibolu
  • 浏览: 1383264 次
文章分类
社区版块
存档分类
最新评论

kernel hacker修炼之道之内存管理-SLAB(分配SLAB对象kmem_cache_alloc())

 
阅读更多

分配SLAB对象kmem_cache_alloc()

作者:李万鹏 于北京 borqs


调用kmem_cache_alloc()来分配空闲对象,如果cpu local slab中没有空闲对象,则从share local slab中填充cpu local slab,如果share local slab也没有空闲对象了,则从slabs_partial或slabs_free中找空闲对象填充share local slab,然后share local slab再填充cpu local slab。注意这里cpu local slab与share local slab并不是存放空闲对象本身,而是存放指向空闲对象的指针,share local slab填充 cpu local slab只是一个指针接管的过程。释放的时候正好相反。share local slab就像一个大缓冲池,cpu local slab用光了从这里取,cpu local slab释放就释放到这里,这样只在必要情况下才影响真正的slab。


void * kmem_cache_alloc (kmem_cache_t *cachep, int flags)
{
return __cache_alloc(cachep, flags);
}

static inline void * __cache_alloc (kmem_cache_t *cachep, int flags)
{
unsigned long save_flags;
void* objp;
struct array_cache *ac;

cache_alloc_debugcheck_before(cachep, flags);

local_irq_save(save_flags);
/*ac_data这个函数的意思是获得cpu local slab cachep->array[smp_processor_id()]*/
ac = ac_data(cachep);
/*如果cpu local slab有对象*/
if (likely(ac->avail)) {
STATS_INC_ALLOCHIT(cachep);
ac->touched = 1;
/*从cpu local slab获得这个对象并移动指针*/
objp = ac_entry(ac)[--ac->avail];
} else {
/*如果cpu local slab没有对象了,从别的地方获得对象*/
STATS_INC_ALLOCMISS(cachep);
objp = cache_alloc_refill(cachep, flags);
}
local_irq_restore(save_flags);
objp = cache_alloc_debugcheck_after(cachep, flags, objp, __builtin_return_address(0));
return objp;
}

static void* cache_alloc_refill(kmem_cache_t* cachep, int flags)
{
int batchcount;
struct kmem_list3 *l3;
struct array_cache *ac;

check_irq_off();
/*获得cpu local slab*/
ac = ac_data(cachep);
retry:
/*需要填充或腾空的块大小*/
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.
*/
batchcount = BATCHREFILL_LIMIT;
}

/* #define list3_data(cachep) (&(cachep)->lists) */
l3 = list3_data(cachep);

BUG_ON(ac->avail > 0);
spin_lock(&cachep->spinlock);
/*如果有共享 local slab*/
if (l3->shared) {
struct array_cache *shared_array = l3->shared;
/*如果共享 local slab 有空闲对象*/
if (shared_array->avail) {
/*更新移动的数量*/
if (batchcount > shared_array->avail)
batchcount = shared_array->avail;
/*share local slab的avail指针下移*/
shared_array->avail -= batchcount;
/*cpu local slab的avail指针上移*/
ac->avail = batchcount;
/*将share local slab中指向空闲对象的指针搬给 cpu local slab*/
memcpy(ac_entry(ac), &ac_entry(shared_array)[shared_array->avail],
sizeof(void*)*batchcount);
/*share local cpu被使用过*/
shared_array->touched = 1;
goto alloc_done;
}
}
/*执行到这里说明share local slab为空,一种情况是一点儿对象都没分配到,一种是分配了部分对象后,share local slab空了,但是还没分配完呢*/
while (batchcount > 0) {
struct list_head *entry;
struct slab *slabp;
/* Get slab alloc is to come from. */
entry = l3->slabs_partial.next;
/*如果slabs_partial和slabs_free都为空的话,只能给cache增加slab了*/
if (entry == &l3->slabs_partial) {
l3->free_touched = 1;
entry = l3->slabs_free.next;
if (entry == &l3->slabs_free)
goto must_grow;
}

slabp = list_entry(entry, struct slab, list);
check_slabp(cachep, slabp);
check_spinlock_acquired(cachep);
/*运行到这里说明从cache的slab中找到了空闲对象*/
while (slabp->inuse < cachep->num && batchcount--) {
kmem_bufctl_t next;
STATS_INC_ALLOCED(cachep);
STATS_INC_ACTIVE(cachep);
STATS_SET_HIGH(cachep);

/*cpu local slab获得空闲对象,上移 cpu local slab的avail指针*/
ac_entry(ac)[ac->avail++] = slabp->s_mem + slabp->free*cachep->objsize;
/*增加slab的使用对象计数*/
slabp->inuse++;
next = slab_bufctl(slabp)[slabp->free];
#if DEBUG
slab_bufctl(slabp)[slabp->free] = BUFCTL_FREE;
#endif /*指向下一个空闲对象*/
slabp->free = next;
}
check_slabp(cachep, slabp);
/*从链表上移除slab,给它找一个目前合适的链表*/
list_del(&slabp->list);
if (slabp->free == BUFCTL_END)
/*如果slab上没有空闲对象了,把它放到slabs_full上*/
list_add(&slabp->list, &l3->slabs_full);
else
/*如果slab上还有对象,则把slab放到slabs_partial链表上*/
list_add(&slabp->list, &l3->slabs_partial);
}

must_grow:
l3->free_objects -= ac->avail;
alloc_done:
spin_unlock(&cachep->spinlock);
/*如果cpu local slab依然没有空闲对象*/
if (unlikely(!ac->avail)) {
int x;
/*则说明cache没有空闲对象了,为cache分配新的空闲slab*/
x = cache_grow(cachep, flags, -1);
/*调用cache_grow()分配新的slab会产生中断,在中断中填充了cpu local slab*/
// cache_grow can reenable interrupts, then ac could change.
ac = ac_data(cachep);
if (!x && ac->avail == 0) // no objects in sight? abort
return NULL;
/*重试获得新对象没成功,那么重复刚才的步骤*/
if (!ac->avail) // objects refilled by interrupt?
goto retry;
}
ac->touched = 1;
/*返回cpu local slab中的对象*/
return ac_entry(ac)[--ac->avail];
}

回答网友问:怎么知道一个slab中的哪些object是空闲的?

每个object存在一个类型为kmem_bufctl_t的object描述符,object描述符存放在一个数组中,实际上是用这个数组形成一个链表。slab->free指向了slab中下一个空闲对象的下标,如果没有剩下空闲对象则为BUFCTL_END。看slab_bufctl函数,slabp+1相当于跳过slab描述符的位置,(char*)slabp + sizeof(*slabp),所以slab_bufctl(slabp)相当于object描述符数组的起始地址,对象描述符中存放的是下一个空闲对象的下表,所以slab_bufctl(slabp)[slabp->free]的值就是下一个空闲对象的下标,将这个下标赋值给slabp->free,就可以用来获得再下一个object描述符了。由于当前这个对象要被使用了从这个链表上移除所以设BUFCTL_FREE,slabp->free = next已经把这个链表头传给了slabp->free。访问空闲对象是这样的:

slabp->s_mem获得slab中第一个对象(或者已被分配,或者空闲)的地址。slab->free中存放的是下一个空闲对象的下标,所以slabp->free * cachep->objectsize


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics