slab源码分析缓存器的创建.docx
《slab源码分析缓存器的创建.docx》由会员分享,可在线阅读,更多相关《slab源码分析缓存器的创建.docx(38页珍藏版)》请在冰点文库上搜索。
![slab源码分析缓存器的创建.docx](https://file1.bingdoc.com/fileroot1/2023-5/8/40a29e4a-2fb2-4899-b9ab-21fba2c8a7f8/40a29e4a-2fb2-4899-b9ab-21fba2c8a7f81.gif)
slab源码分析缓存器的创建
slab源码分析--缓存器的创建
这个函数通常是在内核初始化时进行的,或者在首次加载模块时进行的。
structkmem_cache*kmem_cache_create(constchar*name,size_tsize,size_talign,unsignedlongflags,
void(*ctor)(void*,structkmem_cache*,unsignedlong),void(*dtor)(void*,structkmem_cache*,unsignedlong));
name参数定义了缓存器的名称,proc文件系统(可以cat/proc/slabinfo查看)使用它标识这个缓存。
size参数指定了这个缓存器负责分配的对象大小。
align参数定义了每个对象必须的对齐。
flags参数指定了为缓存启用的选项。
flags的标志如图:
选项说明
RED_ZONE在对象头、尾插入标志,用来支持对缓冲区溢出的检查
SLAB_POISON使用一种已知模式(如0xa5a5a5a5)填充slab,允许对缓存中的对象进行监视,不过可以在外部进行修改)
SLAB_HWCACHE_ALIGN指定缓存对象必须与硬件缓存行对齐
ctor和dtor是构造函数和析构函数,这不用说。
在创建缓存器之后,kmem_cache_create()函数会返回对它的引用。
注意这个函数并没有向缓存器提供任何用来分配对象内存。
相反,在试图从缓存器(最初为空)分配对象时,会通过cache_alloc_refill()函数向伙伴系统申请内存。
当所有对象都被分配出去后,可以再次这样做。
首先给出该函数的调用机制流程图:
kmem_cache_create()函数:
创建缓存器
kmem_cache_zalloc()函数:
申请缓存器缓存
__cache_alloc()函数:
kmem_cache_alloc和kmalloc申请内存的总接口
__do_cache_alloc()函数:
转调函数
____cache_alloc()函数:
申请缓存的核心函数
ac=cpu_cache_get()函数,获得本地缓存
if(av->avail),本地缓存足够
取ac末尾最热数据,objp=ac->entry[--ac->avail]
缓存器缓存申请成功
cache_alloc_refill(),重新填充
if(ls->shared),本地共享缓存足够
transfer_objects()函数,本地共享缓存转给本地缓存
成功向ac中转移了至少一个可用对象
retry:
在三链中搜寻可分配对象
三链中有空闲对象
cache_grow()函数,从伙伴系统获取
kmem_getpages()和alloc_slabmgmt()函数,并执行retry
yes
no
yes
no
yes
no
下面来看kmem_cache_create()函数的实现:
/**
*kmem_cache_create-Createacache.
*@name:
Astringwhichisusedin/proc/slabinfotoidentifythiscache.
*@size:
Thesizeofobjectstobecreatedinthiscache.
*@align:
Therequired(必须的的)alignmentfortheobjects.//
*@flags:
SLABflags
*@ctor:
Aconstructorfortheobjects.
*@dtor:
Adestructorfortheobjects(notimplementedanymore).
*
*Returnsaptrtothecacheonsuccess,NULLonfailure.//成功返回cache指针,失败返回空
*Cannotbecalledwithinaint,butcanbeinterrupted.//不能在中断中调用,但是可以被打断
*The@ctorisrunwhennewpagesareallocatedbythecache
*andthe@dtorisrunbeforethepagesarehandedback.
*
*@namemustbevaliduntilthecacheisdestroyed.Thisimpliesthat
*themodulecallingthishastodestroythecachebeforegettingunloaded.
*
*Theflagsare//填充标记
*
*%SLAB_POISON-Poison(使污染)theslabwithaknowntestpattern(a5a5a5a5)//使用a5a5a5a5填充这片未初始化区域
*tocatchreferencestouninitialisedmemory.
*
*%SLAB_RED_ZONE-Insert`Red'zonesaroundtheallocatedmemorytocheck//添加红色警戒区,检测越界
*forbufferoverruns.
*
*%SLAB_HWCACHE_ALIGN-Aligntheobjectsinthiscachetoahardware//物理缓存行对齐,
*cacheline.Thiscanbebeneficialifyou'recountingcyclesasclosely
*asdavem.
*/
//创建缓存器
/*gfporder:
取值0~11遍历直到计算出cache的对象数量跳出循环,slab由2^gfporder个页面组成
buffer_size:
为当前cache中对象经过cache_line_size对齐后的大小
align:
是cache_line_size,按照该大小对齐
flags:
此处为0,用于标识内置slab还是外置slab
left_over:
输出值,记录slab中浪费空间的大小
num:
输出值,用于记录当前cache中允许存在的对象数目
*/
structkmem_cache*
kmem_cache_create(constchar*name,size_tsize,size_talign,
unsignedlongflags,
void(*ctor)(void*,structkmem_cache*,unsignedlong),
void(*dtor)(void*,structkmem_cache*,unsignedlong))
{
size_tleft_over,slab_size,ralign;
structkmem_cache*cachep=NULL,*pc;
/*
*Sanitychecks...theseareallserioususagebugs.
*/
//参数检查,名字不能为NULL,不能在中断中调用本函数(本函数可能会睡眠)
//获取长度不得小于4字节,即CPU字长,获取长度不得大于最大值(我剖析的这个版本是2^25,有的可能是2^22)
if(!
name||in_interrupt()||(sizesize>KMALLOC_MAX_SIZE||dtor){
printk(KERN_ERR"%s:
Earlyerrorinslab%s\n",__FUNCTION__,
name);
BUG();
}
/*
*Weusecache_chain_mutextoensureaconsistentviewof
*cpu_online_mapaswell.Pleaseseecpuup_callback
*/
mutex_lock(&cache_chain_mutex);
#if0//DEBUG部分被我注释掉了,免得挡点//一些检查机制,无需关注
...
#endif
/*
*Checkthatsizeisintermsof(依据)words.Thisisneededtoavoid
*unalignedaccessesforsomearchs(拱)whenredzoningisused,andmakes//避免当红色警戒区被使用时,避免未对齐的访问接触红区
*sureanyon-slabbufctl'sarealsocorrectlyaligned.//同时确保任何on-slab的bfclt正确对齐
*/
////
//为什么kmem_cache_init函数已经计算过size,align了,这里还要计算?
//因为这里是用来创建缓存器的,只是借用了cache_cache,而kmem_cache_init函数中初始化的是cache_cahce的
//size,align等成员,所以无关系。
////
//先检查对象!
!
!
!
是不是32位对齐,如果不是则进行调整
if(size&(BYTES_PER_WORD-1)){
size+=(BYTES_PER_WORD-1);
size&=~(BYTES_PER_WORD-1);
}
/*calculatethefinalbufferalignment:
*/
/*1)archrecommendation:
canbeoverriddenfordebug*/
//再检查对象!
!
!
要不要求按照缓冲行对齐
if(flags&SLAB_HWCACHE_ALIGN){
/*
*Defaultalignment:
asspecifiedbythearchcode.Exceptif
*anobjectisreallysmall,thensqueezemultipleobjectsinto
*onecacheline.
*/
ralign=cache_line_size();
while(size<=ralign/2)//进行对齐大小的调整,我们要保证对象的大小大于针对硬件缓冲行对齐所需的大小
ralign/=2;
}else{//不需要按硬件缓冲行对齐,那就默认4字节,即32位
ralign=BYTES_PER_WORD;
}
/*
*Redzoninganduserstorerequirewordalignmentorpossiblylarger.
*Notethiswillbeoverriddenbyarchitectureorcallermandated
*alignmentifeitherisgreaterthanBYTES_PER_WORD.
*/
//如果开启了DEBUG,则按需要进行相应的对齐
if(flags&SLAB_STORE_USER)
ralign=BYTES_PER_WORD;
if(flags&SLAB_RED_ZONE){
ralign=REDZONE_ALIGN;
/*Ifredzoning,ensurethatthesecondredzoneissuitably
*aligned,byadjustingtheobjectsizeaccordingly.*/
size+=REDZONE_ALIGN-1;
size&=~(REDZONE_ALIGN-1);
}
/*2)archmandatedalignment*/
if(ralignralign=ARCH_SLAB_MINALIGN;
}
/*3)callermandatedalignment*/
if(ralignralign=align;
}
/*disabledebugifnecessary*/
if(ralign>__alignof__(unsignedlonglong))
flags&=~(SLAB_RED_ZONE|SLAB_STORE_USER);
/*
*4)Storeit.
*/
align=ralign;//通过上面一大堆计算,算出了align值
/*Getcache'sdescriptionobj.*/
//按照cache_cache的大小分配一个kmem_cache新实例,实际上cache_cache在内核初始化完成后就是kmem_cache了,为了内核初始化时可使用kmalloc,所以这里要用cache_cache
cachep=kmem_cache_zalloc(&cache_cache,GFP_KERNEL);//哈哈,这就是使用cache_cache
//这里会分配一块干净的清零过的内存
if(!
cachep)
gotooops;
#ifDEBUG
...
#endif
/*
*Determineiftheslabmanagementis'on'or'off'slab.
*(bootstrappingcannotcopewithoffslabcachessodon'tdo
*ittooearlyon.)
*/
//第一个条件通过PAGE_SIZE确定slab管理对象的存储方式,内置还是外置。
//初始化阶段采用内置式(kmem_cache_init()中创建两个普通高速缓存后就把slab_early_init置0了
if((size>=(PAGE_SIZE>>3))&&!
slab_early_init)
/*
*Sizeislarge,assumebesttoplacetheslabmanagementobj
*off-slab(shouldallowbetterpackingofobjs).
*/
flags|=CFLGS_OFF_SLAB;
size=ALIGN(size,align);//从这一步可知,slab机制先把对象针对及其字长进行对齐,然后再在此基础上又针对硬件缓冲行进行对齐。
//以后所有的对齐都要照这个总的对齐值对齐
//计算碎片大小,计算slab由几个页面(order)组成,同时计算每个slab中有多少个对象
left_over=calculate_slab_order(cachep,size,align,flags);//这次计算的不是cache_cache了
if(!
cachep->num){
printk(KERN_ERR
"kmem_cache_create:
couldn'tcreatecache%s.\n",name);
kmem_cache_free(&cache_cache,cachep);
cachep=NULL;
gotooops;
}
//计算slab管理对象的大小,包括structslab对象和kmem_bufctl_t数组
slab_size=ALIGN(cachep->num*sizeof(kmem_bufctl_t)
+sizeof(structslab),align);
/*
*Iftheslabhasbeenplacedoff-slab,andwehaveenoughspacethen
*moveiton-slab.Thisisattheexpenseofanyextracolouring.
*/
//如果是一个外置slab,并且碎片大小大于slab管理对象的大小,则可将slab管理对象移到slab中,改造成一个内置slab!
!
!
!
!
if(flags&CFLGS_OFF_SLAB&&left_over>=slab_size){
flags&=~CFLGS_OFF_SLAB;
left_over-=slab_size;//slab_size就是slab管理对象大小
}
if(flags&CFLGS_OFF_SLAB){
//align是针对slab对象的,如果slab管理者是外置存储,自然也不会像内置那样影响到后面slab对象的存储位置
//slab管理者也就不需要对齐了
/*reallyoffslab.Noneedformanualalignment*/
slab_size=
cachep->num*sizeof(kmem_bufctl_t)+sizeof(structslab);
}
//着色块单位,为L1_CACHE_BYTES,即32字节
cachep->colour_off=cache_line_size();
/*Offsetmustbeamultipleofthealignment.*/
//着色单位必须是对齐单位的整数倍
if(cachep->colour_offcachep->colour_off=align;
//计算碎片区域需要多少个着色块
cachep->colour=left_over/cachep->colour_off;
//管理对象的大小
cachep->slab_size=slab_size;
cachep->flags=flags;
cachep->gfpflags=0;
if(CONFIG_ZONE_DMA_FLAG&&(flags&SLAB_CACHE_DMA))//与伙伴系统交互的DMA标志
cachep->gfpflags|=GFP_DMA;
//slab对象的大小
cachep->buffer_size=size;
//倒数
cachep->reciprocal_buffer_size=reciprocal_value(size);
//如果是外置slab,这里要分配一个管理对象,保存在slabp_cache中,如果是内置式的slab,此指针为空
//array_cachine,cache_cache,3list这几个肯定是内置式,不会进入这个
if(flags&CFLGS_OFF_SLAB){
cachep->slabp_cache=kmem_find_general_cachep(slab_size,0u);
/*
*Thisisapossibilityforoneofthemalloc_sizescaches.
*Butsincewegooffslabonlyforobjectsizegreaterthan
*PAGE_SIZE/8,andmalloc_sizesgetscreatedinascendingorder,
*thisshouldnothappenatall.
*ButleaveaBUG_ONforsomeluckydude.
*/
BUG_ON(!
cachep->slabp_cache);
}
//kmem_cach的名字和它管理的对象的构造函数
cachep->ctor=ctor;
cachep->name=name;
//设置每个CPU上的localcache,配置localcache和slab三链
if(setup_cpu_cache(cachep)){
__kmem_cache_destroy(cachep);
cachep=NULL;
gotooops;
}
//将kmem_cache加入到cache_chain为头的kmem_cache链表中
/*cachesetupcompleted,linkitintothelist*/
list_add(&cachep->next,&cache_chain);//还是用了cache_chain
oops:
if(!
cachep&&(flags&SLAB_PANIC))
panic("kmem_cache_create():
failedtocreateslab`%s'\n",
name);
mutex_unlock(&cache_chain_mutex);//mutex
returncachep;//返回该kmem_cache
}
在这个函数中,首先要计算一些对齐的值。
一是内存对齐,我们需要数据按照CPU字长进行对齐(比如结构体中间一个char类型数据32位下所占字节可能是4字节),这样才能提高CPU访问数据的效率。
其次如果设置了SLAB_HWCACHE_ALIGN,那么还要和缓存行(cachineline)进行对齐。
和缓存行除此之外,除了需要DEBUG,可能还需要一些对齐,不过这都不是我们关注的重点。
总之,前面一大串就是计算出了slab的对齐值。
然后调用kmem_cache_zalloc()函数为要创建的缓存器申请内存,我们知道,先前我们定义了cache_cache这个缓存器的缓存器,此时就派上用场了,这里就用它来做参数。
/**
*kmem_cache_zalloc-Allocateanobject.Thememoryissettozero.
*@cache:
Thecachetoallocatefrom.
*@flags:
Seekmalloc().
*
*Allocateanobjectfromthiscacheandsettheallocatedmemorytozero.
*Theflagsareonlyrelevantifthecachehasno