void*objp=objpp[i];
structslab*slabp;
//通过对象的虚拟地址得到page,再通过page得到slab
slabp=virt_to_slab(objp);
//获得slab三链
l3=cachep->nodelists[node];
//先将对象所在的slab从链表中摘除
list_del(&slabp->list);
check_spinlock_acquired_node(cachep,node);
check_slabp(cachep,slabp);
//将对象放到其slab中
slab_put_obj(cachep,slabp,objp,node);
STATS_DEC_ACTIVE(cachep);
//增加空闲对象计数
l3->free_objects++;
check_slabp(cachep,slabp);
/*fixupslabchains*/
//如果slab中全都是空闲对象
if(slabp->inuse==0){
//如果三链中空闲对象数目超过上限,直接回收整个slab到内存,空闲对象数减去每个slab中对象数
if(l3->free_objects>l3->free_limit){
l3->free_objects-=cachep->num;
/*Noneedtodropanypreviouslyheld
*lockhere,evenifwehaveaoff-slabslab
*descriptoritisguaranteedtocomefrom
*adifferentcache,refertocommentsbefore
*alloc_slabmgmt.
*/
//销毁slab对象
slab_destroy(cachep,slabp);
}else{//到这里说明空闲对象数目还没有超过三链设置的上限
//只需将此slab添加到空slab链表中
list_add(&slabp->list,&l3->slabs_free);
}
}else{
/*Unconditionallymoveaslabtotheendofthe
*partiallistonfree-maximumtimeforthe
*otherobjectstobefreed,too.
*/
//将此slab添加到部分满的链表中
list_add_tail(&slabp->list,&l3->slabs_partial);
}
}
}
整个流程就是这样,回收优先级:
本地缓存>本地共享缓存>三链。
销毁slab
销毁slab就是释放slab管理区和对象占用的空间,还给伙伴系统。
**
*slab_destroy-destroyandreleaseallobjectsinaslab
*@cachep:
cachepointerbeingdestroyed
*@slabp:
slabpointerbeingdestroyed
*
*Destroyalltheobjsinaslab,andreleasethemembacktothesystem.
*Beforecallingtheslabmusthavebeenunlinkedfromthecache.The
*cache-lockisnotheld/needed.
*/
//销毁slab,需要释放管理对象和slab对象
staticvoidslab_destroy(structkmem_cache*cachep,structslab*slabp)
{
//获得slab页面的首地址,是用第一个对象的地址colouroff(对于内置式slab,colouroff已经将slab管理者包括在内了)
void*addr=slabp->s_mem-slabp->colouroff;
//debug用
slab_destroy_objs(cachep,slabp);
//使用SLAB_DESTROY_BY_RCU来创建的高速缓存
if(unlikely(cachep->flags&SLAB_DESTROY_BY_RCU)){
//rcu方式释放,暂时不做分析,主要是做并行优化
structslab_rcu*slab_rcu;
slab_rcu=(structslab_rcu*)slabp;
slab_rcu->cachep=cachep;
slab_rcu->addr=addr;
//注册一个回调来延期释放slab
call_rcu(&slab_rcu->head,kmem_rcu_free);
}else{
//释放slab占用的页面到伙伴系统中
//如果是内置式,slab管理者和slab对象在一起,可以同时释放
kmem_freepages(cachep,addr);
if(OFF_SLAB(cachep))
//外置式,还需释放slab管理对象
kmem_cache_free(cachep->slabp_cache,slabp);
}
}
与伙伴系统交互的函数暂不再本文讨论范围之内。
销毁缓存器
销毁缓存器首先要保证的一点就是当前缓存器中所有的对象都是空闲的,也就是之前分配出去的对象都已经释放回来了,其主要的步骤如下:
将缓存器kmem_cache从cache_chain链表中删除。
将本地高速缓存,align高速缓存和本地共享缓存中的对象都回收到slab三链,并释放所有的free链表,然后判断full链表以及partial链表是否都为空,如果有一个不为空说明存在非空闲slab,也就是说**还有对象未释放,此时无法销毁缓存器,必须重新将缓存器添加到cache_chain链表中。
确定所有的对象都为空闲状态后,将缓存器涉及到的所有描述符都释放(这些描述符都是保存在通用缓存器中的,如slab管理者)。
负责销毁缓存器的函数为kmem_cache_destroy():
/**
*kmem_cache_destroy-deleteacache
*@cachep:
thecachetodestroy
*
*Removea&structkmem_cacheobjectfromtheslabcache.
*
*Itisexpectedthisfunctionwillbecalledbyamodulewhenitis
*unloaded.Thiswillremovethecachecompletely,andavoidaduplicate
*cachebeingallocatedeachtimeamoduleisloadedandunloaded,ifthe
*moduledoesn'thavepersistentin-kernelstorageacrossloadsandunloads.
*
*Thecachemustbeemptybeforecallingthisfunction.
*
*Thecallermustguaranteethatnoonewillallocatememoryfromthecache
*duringthekmem_cache_destroy().
*/
//销毁一个缓存器,通常这只发生在卸载module时
voidkmem_cache_destroy(structkmem_cache*cachep)
{
BUG_ON(!
cachep||in_interrupt());
/*Findthecacheinthechainofcaches.*/
mutex_lock(&cache_chain_mutex);
/*
*thechainisneverempty,cache_cacheisneverdestroyed
*/
//将缓存器从cache_chain的链表中摘除
list_del(&cachep->next);
if(__cache_shrink(cachep)){//释放空链表中的slab,并检查其他两个链表。
在销毁缓存器前,必须先销毁其中的slab
//满slab链或部分满slab链不为空
slab_error(cachep,"Can'tfreeallobjects");
//缓存器非空,不能销毁,重新加入到cache_chain链表中
list_add(&cachep->next,&cache_chain);
mutex_unlock(&cache_chain_mutex);
return;
}
//有关rcu
if(unlikely(cachep->flags&SLAB_DESTROY_BY_RCU))
synchronize_rcu();
//底层调用__kmem_cache_destroy()函数来实现
__kmem_cache_destroy(cachep);
mutex_unlock(&cache_chain_mutex);
}
释放空链表的slab由下面这个函数负责:
/*Calledwithcache_chain_mutexheldtoprotectagainstcpuhotplug*/
//释放空链表中的slab
staticint__cache_shrink(structkmem_cache*cachep)
{
intret=0,i=0;
structkmem_list3*l3;
//释放本地缓存中对象
drain_cpu_caches(cachep);
check_irq_on();
for_each_online_node(i){
l3=cachep->nodelists[i];
if(!
l3)
continue;
//释放空链表中的slab
drain_freelist(cachep,l3,l3->free_objects);
//检查满slab链表和部分满slab链表是否还有slab
ret+=!
list_empty(&l3->slabs_full)||
!
list_empty(&l3->slabs_partial);
}
return(ret?
1:
0);
}
其中用到了这几个函数:
//释放本地缓存和本地共享缓存中的对象
staticvoiddrain_cpu_caches(structkmem_cache*cachep)
{
structkmem_list3*l3;
intnode;
//释放每个本地缓存中的对象,注意没有"online"
on_each_cpu(do_drain,cachep,1,1);//调用了do_drain()函数
check_irq_on();
//NUMA相关,释放每个NUMA节点的alien
for_each_online_node(node){
l3=cachep->nodelists[node];
if(l3&&l3->alien)
//本版本目前是空函数,暂不支持
drain_alien_cache(cachep,l3->alien);
}
//释放本地共享缓存中的对象
for_each_online_node(node){
l3=cachep->nodelists[node];
if(l3)
drain_array(cachep,l3,l3->shared,1,node);
}
}
//释放本地缓存中的对象
staticvoiddo_drain(void*arg)
{
structkmem_cache*cachep=arg;
structarray_cache*ac;
intnode=numa_node_id();
check_irq_off();
//获得本地缓存
ac=cpu_cache_get(cachep);
spin_lock(&cachep->nodelists[node]->list_lock);
//释放本地缓存中的对象
free_block(cachep,ac->entry,ac->avail,node);
spin_unlock(&cachep->nodelists[node]->list_lock);
ac->avail=0;
}
/*
*Drainanarrayifitcontainsanyelementstakingthel3lockonlyif
*necessary.Notethatthel3listlockalsoprotectsthearray_cache
*ifdrain_array()issedonthesharedarray.
*/
voiddrain_array(structkmem_cache*cachep,structkmem_list3*l3,
structarray_cache*ac,intforce,intnode)
{
inttofree;
if(!
ac||!
ac->avail)
return;
if(ac->touched&&!
force){
ac->touched=0;
}else{
spin_lock_irq(&l3->list_lock);
if(ac->avail){
//计算释放对象的数目,可见这个函数还支持部分释放,取决于force的bool属性
//从drain_cpu_caches()进入时,force=1,是要全部释放的
tofree=force?
ac->avail:
(ac->limit+4)/5;
if(tofree>ac->avail)
tofree=(ac->avail+1)/2;
//释放对象,从entry前面开始
free_block(cachep,ac->entry,tofree,node);
ac->avail-=tofree;
//后面的对象前移
memmove(ac->entry,&(ac->entry[tofree]),
sizeof(void*)*ac->avail);
}
spin_unlock_irq(&l3->list_lock);
}
}
drain_array()函数是把本地缓存和本地共享缓存释放到三链中,所以会用到:
/*
*Callerneedstoacquirecorrectkmem_list'slist_lock
*///释放一定数目的对象
staticvoidfree_block(structkmem_cache*cachep,void**objpp,intnr_objects,
intnode)
{
inti;
structkmem_list3*l3;
//逐一释放对象到三链中
for(i=0;ivoid*objp=objpp[i];
structslab*slabp;
//通过对象的虚拟地址得到page,再通过page得到slab
slabp=virt_to_slab(objp);
//获得slab三链