linux内存管理slab算法之kmem_cache结构创建

来源:互联网 发布:网站维护页面源码 编辑:程序博客网 时间:2024/06/01 11:08

kmem_cache是slab的核心结构体,主要描述slab的各种信息和链接空闲slab,还保存高速缓存的指针数组。所以要想使用slab分配得先创建kmem_cache结构体。

struct kmem_cache *kmem_cache_create (const char *name, size_t size, size_t align,unsigned long flags, void (*ctor)(void *)){size_t left_over, slab_size, ralign;struct kmem_cache *cachep = NULL, *pc;if (!name || (size < BYTES_PER_WORD) ||    size > KMALLOC_MAX_SIZE) {printf("slab create error\n");} //size和字对齐,32位4字节对齐,64位8字节对齐if (size & (BYTES_PER_WORD - 1)) {size += (BYTES_PER_WORD - 1);size &= ~(BYTES_PER_WORD - 1);}//如果标记了cache对齐,cache行大小在现代的处理器中大部分是64Bif (flags & SLAB_HWCACHE_ALIGN) {ralign = cache_line_size(); //获取cache行大小while (size <= ralign / 2)  //获取size最小对齐ralign /= 2;} else {ralign = BYTES_PER_WORD;}    /* 特定体系结构最小的slab对象大小*/if (ralign < ARCH_SLAB_MINALIGN) {ralign = ARCH_SLAB_MINALIGN;}if (ralign < align) {ralign = align;    //取更大的对齐}/* * 计算完成对齐 */align = ralign;//获取kmem_cache结构体指针,从cache_cache结构体中分配cachep = kmem_cache_zalloc(&cache_cache, GFP_KERNEL);if (!cachep)goto oops; //如果申请的对象size大于等于512B,则slab管理结构不在本slab页面上,在早期初始化slab阶段所分配的cache都是onslab的 //早期初始化完成后可以offslabif ((size >= (PAGE_SIZE >> 3)) && !slab_early_init)/* * Size is large, assume best to place the slab management obj * off-slab (should allow better packing of objs). */ //如果分配的对象很大,则最好将slab管理结构放在slab外面,也就是动态的从其它cache分配一块flags |= CFLGS_OFF_SLAB;    //对齐申请的sizesize = ALIGN(size, align);    //计算剩余字节和一个slab的页面的阶left_over = calculate_slab_order(cachep, size, align, flags);//如果对象个数是0则说明分配出错,内存不够了if (!cachep->num) {printf(       "kmem_cache_create: couldn't create cache %s.\n", name);cachep = NULL;goto oops;}//计算对齐后的slab管理结构的大小slab_size = ALIGN(cachep->num * sizeof(kmem_bufctl_t)  + sizeof(struct slab), align); //如果slab管理结构在本cache外面,并且剩余的字节数大于等于管理结构体的 //大小则把标记位置为onslab,并且剩余字节数减去slab管理结构的大小 //这样做应该是为了节省空间,减少内部碎片,但是可能会造成比较多的cache missif (flags & CFLGS_OFF_SLAB && left_over >= slab_size) {flags &= ~CFLGS_OFF_SLAB;left_over -= slab_size;}    //如果slab管理结构不在本cache上,则slab管理结构的大小不需要对齐,只需要计算真实的slab管理结构大小if (flags & CFLGS_OFF_SLAB) {/* really off slab. No need for manual alignment */slab_size =    cachep->num * sizeof(kmem_bufctl_t) + sizeof(struct slab);}    //着色偏移等于cache line的大小cachep->colour_off = cache_line_size();/* Offset must be a multiple of the alignment. *///如果指定的对齐大于cache line大小,则着色偏移等于指定对齐if (cachep->colour_off < align)cachep->colour_off = align;    //着色颜色个数等于剩余字节数除以着色偏移cachep->colour = left_over / cachep->colour_off;//slab管理结构的大小cachep->slab_size = slab_size;//标记位cachep->flags = flags;cachep->gfpflags = 0;//cache中的object大小cachep->buffer_size = size;//cache对象大小的倒数cachep->reciprocal_buffer_size = reciprocal_value(size);    //如果slab管理结构不在本cache,则需要给slab管理结构指定一个大小适合的kmem_cache    //给slab管理结构分配内存if (flags & CFLGS_OFF_SLAB) {cachep->slabp_cache = kmem_find_general_cachep(slab_size, 0u);/* * This is a possibility for one of the malloc_sizes caches. * But since we go off slab only for object size greater than * PAGE_SIZE/8, and malloc_sizes gets created in ascending order, * this should not happen at all. * But leave a BUG_ON for some lucky dude. *///BUG_ON(ZERO_OR_NULL_PTR(cachep->slabp_cache));}//构造函数指针,分配完回调cachep->ctor = ctor;//kmem_cache的名字cachep->name = name;//设置cache的array缓存if (setup_cpu_cache(cachep)) {__kmem_cache_destroy(cachep);cachep = NULL;goto oops;}/* cache setup completed, link it into the list *///把分配的cache加入到kmem_cache链表list_add(&cachep->next, &cache_chain);oops:    //如果cachep指针为空,则说明没分配成功报错if (!cachep && (flags & SLAB_PANIC))printf("kmem_cache_create(): failed to create slab `%s'\n",      name);//mutex_unlock(&cache_chain_mutex);//put_online_cpus();    //返回cachepreturn cachep;}
接下来主要分析calculate_slab_order函数,此函数主要计算一个slab占用的页面个数,以最小对象个数为单位。

static size_t calculate_slab_order(struct kmem_cache *cachep,size_t size, size_t align, unsigned long flags){unsigned long offslab_limit;size_t left_over = 0;int gfporder;//计算一个slab占用页面的最小阶for (gfporder = 0; gfporder <= KMALLOC_MAX_ORDER; gfporder++) {unsigned int num;size_t remainder;cache_estimate(gfporder, size, align, flags, &remainder, &num);if (!num)continue;if (flags & CFLGS_OFF_SLAB) {/* * Max number of objs-per-slab for caches which * use off-slab slabs. Needed to avoid a possible * looping condition in cache_grow(). */ /*  *如果slab管理结构不在本slab页面上则需要计算slab大小的限制,主要针对  *大内存对象  */offslab_limit = size - sizeof(struct slab);offslab_limit /= sizeof(kmem_bufctl_t); if (num > offslab_limit)break;}//一个slab对象个数cachep->num = num;//一个slab占用的页面的个数的阶cachep->gfporder = gfporder;//内部碎片大小left_over = remainder;/* * A VFS-reclaimable slab tends to have most allocations * as GFP_NOFS and we really don't want to have to be allocating * higher-order pages when we are unable to shrink dcache. *///if (flags & SLAB_RECLAIM_ACCOUNT)//break;/* * Large number of objects is good, but very large slabs are * currently bad for the gfp()s. */ //一个slab按照最小的页面数计算,比如不超过4KB的对象,每次分配slab只需要 //一页即可if (gfporder >= slab_break_gfp_order)break;/* * Acceptable internal fragmentation? */ //内部碎片乘以8,要小于等于所分配的页大小 //比如此时只分配了一页,那么leftover的大小不能大于512Bif (left_over * 8 <= (PAGE_SIZE << gfporder))break;}return left_over;}

具体的计算函数cache_estimate

//计算一个slab中的object的数目和slab剩余的字节数static void cache_estimate(unsigned long gfporder, size_t buffer_size,   size_t align, int flags, size_t *left_over,   unsigned int *num){int nr_objs;size_t mgmt_size;size_t slab_size = PAGE_SIZE << gfporder;//如果slab管理结构不在本slab页面上if (flags & CFLGS_OFF_SLAB) {mgmt_size = 0;nr_objs = slab_size / buffer_size; //一个slab上对象的个数if (nr_objs > SLAB_LIMIT)nr_objs = SLAB_LIMIT; //如果超过个数限制就等于最大的} else {//如果在本页面上//计算对象个数nr_objs = (slab_size - sizeof(struct slab)) /  (buffer_size + sizeof(kmem_bufctl_t));/* * This calculated number will be either the right * amount, or one greater than what we want. */ //计算的结构可能超过slab_size大小,需要减去一个对象if (slab_mgmt_size(nr_objs, align) + nr_objs*buffer_size       > slab_size)nr_objs--;if (nr_objs > SLAB_LIMIT)nr_objs = SLAB_LIMIT;//slab管理结构的最终大小mgmt_size = slab_mgmt_size(nr_objs, align);}*num = nr_objs;//内部碎片大小*left_over = slab_size - nr_objs*buffer_size - mgmt_size;}



阅读全文
0 0