分类:
2011-12-16 20:39:58
原文地址:Linux内存管理之slab分配器分析(续四) 作者:xgr180
七:kmem_cache_free()的实现
kmem_cache_free用于把从slab中分配的对象释放掉,同分配一样,它首先会把它放到AC中,如果AC满了,则把对象释放到share链中,如果share也满了,也就把它释放至slab。来看具体的代码:
void kmem_cache_free (kmem_cache_t *cachep, void *objp)
{
unsigned long flags;
local_irq_save(flags);
__cache_free(cachep, objp);
local_irq_restore(flags);
}
函数调用__cache_free(),代码如下:
static inline void __cache_free (kmem_cache_t *cachep, void* objp)
{
struct array_cache *ac = ac_data(cachep);
check_irq_off();
//DEBUG用,忽略
objp = cache_free_debugcheck(cachep, objp, __builtin_return_address(0));
//如果AC中的对像没有超过限制,那就把它释放到AC中。
if (likely(ac->avail < ac->limit)) {
STATS_INC_FREEHIT(cachep);
ac_entry(ac)[ac->avail++] = objp;
return;
} else {
//如果AC中对象数目到了限值,则cache_flusharray()后,再把对像加入AC中
STATS_INC_FREEMISS(cachep);
cache_flusharray(cachep, ac);
ac_entry(ac)[ac->avail++] = objp;
}
}
如果当前AC中对象数已经到达限值,就会调用cache_flusharray()将里面的对象“刷新”出去。代码如下:
static void cache_flusharray (kmem_cache_t* cachep, struct array_cache *ac)
{
int batchcount;
//要从AC中转移的对象数
batchcount = ac->batchcount;
#if DEBUG
BUG_ON(!batchcount || batchcount > ac->avail);
#endif
check_irq_off();
spin_lock(&cachep->spinlock);
if (cachep->lists.shared) {
struct array_cache *shared_array = cachep->lists.shared;
int max = shared_array->limit-shared_array->avail;
if (max) {
//如果share中还有可闲
if (batchcount > max)
batchcount = max;
//将AC中的对象复制到share中
memcpy(&ac_entry(shared_array)[shared_array->avail],
&ac_entry(ac)[0],
sizeof(void*)*batchcount);
shared_array->avail += batchcount;
goto free_done;
}
}
//运行到这里。说明share也满了,只能把对象归还slab了
free_block(cachep, &ac_entry(ac)[0], batchcount);
free_done:
//STATS只有在打开DEBUG开关的时候才会为1,起DEBUG作用,忽略之
#if STATS
{
int i = 0;
struct list_head *p;
p = list3_data(cachep)->slabs_free.next;
while (p != &(list3_data(cachep)->slabs_free)) {
struct slab *slabp;
slabp = list_entry(p, struct slab, list);
BUG_ON(slabp->inuse);
i++;
p = p->next;
}
STATS_SET_FREEABLE(cachep, i);
}
#endif
spin_unlock(&cachep->spinlock);
//更新avail计数
ac->avail -= batchcount;
//从前面已经知道,实际上AC中的0~batchcount项都已经转移到share中了
//所以,把batchcount后面的项移到前面
memmove(&ac_entry(ac)[0], &ac_entry(ac)[batchcount],
sizeof(void*)*ac->avail);
}
free_block()用于将对象归还给slab,代码如下:
static void free_block(kmem_cache_t *cachep, void **objpp, int nr_objects)
{
int i;
check_spinlock_acquired(cachep);
//更新free_objects计数
cachep->lists.free_objects += nr_objects;
for (i = 0; i < nr_objects; i++) {
void *objp = objpp[i];
struct slab *slabp;
unsigned int objnr;
//取得对象所在的slab
slabp = GET_PAGE_SLAB(virt_to_page(objp));
list_del(&slabp->list);
//对象在slab中的序号
objnr = (objp - slabp->s_mem) / cachep->objsize;
check_slabp(cachep, slabp);
#if DEBUG
if (slab_bufctl(slabp)[objnr] != BUFCTL_FREE) {
printk(KERN_ERR "slab: double free detected in cache '%s', objp %p.\n",
cachep->name, objp);
BUG();
}
#endif
//更新bufctl
slab_bufctl(slabp)[objnr] = slabp->free;
slabp->free = objnr;
STATS_DEC_ACTIVE(cachep);
slabp->inuse--;
check_slabp(cachep, slabp);
//调整slab所在的链表
if (slabp->inuse == 0) {
//如果slabp中没有被使用的对像
if (cachep->lists.free_objects > cachep->free_limit) {
//如果cahce中空闲对象数超过限值,则把slab释放掉
cachep->lists.free_objects -= cachep->num;
slab_destroy(cachep, slabp);
} else {
//把slab移到“全空”链表
list_add(&slabp->list,
&list3_data_ptr(cachep, objp)->slabs_free);
}
} else {
//把slabp加入到slabs_partial末尾
list_add_tail(&slabp->list,
&list3_data_ptr(cachep, objp)->slabs_partial);
}
}
}
//slab_destroy用于将slabp所点的空间全部都释放掉
static void slab_destroy (kmem_cache_t *cachep, struct slab *slabp)
{
//将slabp->s_mem前移colouroff位,即为slab的起始地址
void *addr = slabp->s_mem - slabp->colouroff;
//忽略掉DEBUG
#if DEBUG
int i;
for (i = 0; i < cachep->num; i++) {
void *objp = slabp->s_mem + cachep->objsize * i;
if (cachep->flags & SLAB_POISON) {
#ifdef CONFIG_DEBUG_PAGEALLOC
if ((cachep->objsize%PAGE_SIZE)==0 && OFF_SLAB(cachep))
kernel_map_pages(virt_to_page(objp), cachep->objsize/PAGE_SIZE,1);
else
check_poison_obj(cachep, objp);
#else
check_poison_obj(cachep, objp);
#endif
}
if (cachep->flags & SLAB_RED_ZONE) {
if (*dbg_redzone1(cachep, objp) != RED_INACTIVE)
slab_error(cachep, "start of a freed object "
"was overwritten");
if (*dbg_redzone2(cachep, objp) != RED_INACTIVE)
slab_error(cachep, "end of a freed object "
"was overwritten");
}
if (cachep->dtor && !(cachep->flags & SLAB_POISON))
(cachep->dtor)(objp+obj_dbghead(cachep), cachep, 0);
}
#else
//为其中的每一对象调用析构函数
if (cachep->dtor) {
int i;
for (i = 0; i < cachep->num; i++) {
void* objp = slabp->s_mem+cachep->objsize*i;
(cachep->dtor)(objp, cachep, 0);
}
}
#endif
if (unlikely(cachep->flags & SLAB_DESTROY_BY_RCU)) {
struct slab_rcu *slab_rcu;
slab_rcu = (struct slab_rcu *) slabp;
slab_rcu->cachep = cachep;
slab_rcu->addr = addr;
call_rcu(&slab_rcu->head, kmem_rcu_free);
} else {
//将内存归还伙伴系统
kmem_freepages(cachep, addr);
if (OFF_SLAB(cachep))
//如果slab是外置的,则将slab则相应的cache中释放掉
kmem_cache_free(cachep->slabp_cache, slabp);
}
}
八:kmem_cache_destroy()实现:
Kmem_cache_destroy先会将AC和share中的对象释放到slab中,再把每一个slab都释放掉,如果当前cache中没有被分配对象的话,就会释放掉cache描述符,AC和share。代码如下:
int kmem_cache_destroy (kmem_cache_t * cachep)
{
int i;
//参数为空,或者在中断处理中。
if (!cachep || in_interrupt())
BUG();
/* Don't let CPUs to come and go */
lock_cpu_hotplug();
/* Find the cache in the chain of caches. */
down(&cache_chain_sem);
//将cache脱链
list_del(&cachep->next);
up(&cache_chain_sem);
//_cache_shrink(cachep):将cachep中的所有slabp全都释放掉
if (__cache_shrink(cachep)) {
//如果当前cache中还有被分配对象,返回
slab_error(cachep, "Can't free all objects");
down(&cache_chain_sem);
//重新加入链表
list_add(&cachep->next,&cache_chain);
up(&cache_chain_sem);
unlock_cpu_hotplug();
return 1;
}
if (unlikely(cachep->flags & SLAB_DESTROY_BY_RCU))
synchronize_kernel();
//释放掉AC
for (i = 0; i < NR_CPUS; i++)
kfree(cachep->array[i]);
//释放掉share
kfree(cachep->lists.shared);
cachep->lists.shared = NULL;
//将cache描述符也释放掉
kmem_cache_free(&cache_cache, cachep);
unlock_cpu_hotplug();
return 0;
}
接着看_cache_shrink():
static int __cache_shrink(kmem_cache_t *cachep)
{
struct slab *slabp;
int ret;
//将AC share中的对象全都释放给slab.我们在前面的代码中看到,释放对象时,先将对象释放到
//AC中,如果AC满了,再释放到share中,若是share满了,才会释放到slab.当cache要销毁的时候,//这些缓冲中的对象用不着了,先释放到slab中,再把整个slab释放掉
drain_cpu_caches(cachep);
check_irq_on();
spin_lock_irq(&cachep->spinlock);
//将slabs_free中的slab全被释放掉
for(;;) {
struct list_head *p;
p = cachep->lists.slabs_free.prev;
if (p == &cachep->lists.slabs_free)
break;
slabp = list_entry(cachep->lists.slabs_free.prev, struct slab, list);
#if DEBUG
if (slabp->inuse)
BUG();
#endif
list_del(&slabp->list);
cachep->lists.free_objects -= cachep->num;
spin_unlock_irq(&cachep->spinlock);
//这函数我们在前面已经分析过了的
slab_destroy(cachep, slabp);
spin_lock_irq(&cachep->spinlock);
}
//如果cachep->lists.slabs_ful和slabs_partial还有对象,说明cache中还被分配的对象,
ret = !list_empty(&cachep->lists.slabs_full) ||
!list_empty(&cachep->lists.slabs_partial);
spin_unlock_irq(&cachep->spinlock);
return ret;
}