您的位置:首页 > 其它

内存管理-SLAB(分配SLAB对象kmem_cache_alloc())

2012-05-07 22:36 417 查看
调用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是空闲的?

[cpp] view
plaincopy

static inline kmem_bufctl_t *slab_bufctl(struct slab *slabp)

{

return (kmem_bufctl_t *)(slabp+1);

}

kmem_bufctl_t next;

next = slab_bufctl(slabp)[slabp->free];

slab_bufctl(slabp)[slabp->free] = BUFCTL_FREE;

slabp->free = next;

每个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。访问空闲对象是这样的:

[cpp] view
plaincopy

slabp->s_mem + slabp->free * cachep->objectsize

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



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: