Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2738508
  • 博文数量: 79
  • 博客积分: 30130
  • 博客等级: 大将
  • 技术积分: 2608
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-22 14:58
个人简介

博所搬至http://xiaogr.com

文章存档

2015年(2)

2009年(3)

2008年(56)

2007年(18)

分类: LINUX

2008-02-01 16:43:58

五:kmem_cache_create()分析

我们以一个例子来跟踪分析一下slab的机制:

下面是一个测试模块的代码:

#include

#include

#include

 

MODULE_LICENSE("GPL");

MODULE_AUTHOR("ericxiao ");

MODULE_DESCRIPTION("slab test module");

 

static kmem_cache_t *test_cachep = NULL;

struct slab_test

{

       int val;

};

 

void fun_ctor(struct slab_test *object , kmem_cache_t *cachep , unsigned long flags )

{

                printk("in ctor fuction ...\n");

                object->val = 1;

}

 

void fun_dtor(struct slab_test *object , kmem_cache_t *cachep , unsigned long flags)

{

     printk("in dtor fuction ...\n");

     object -> val = 0;

}

 

static int __init init(void)

{

       struct slab_test *object = NULL;

       printk("slab test moudle init ... \n");

       test_cachep = kmem_cache_create("test_cachep",sizeof(struct slab_test),0,SLAB_HWCACHE_ALIGN, \

                    fun_ctor, fun_dtor);

       if(!test_cachep)

                        return;            

       object = kmem_cache_alloc( test_cachep, GFP_KERNEL );

       if(object)

       {

                 printk("alloc one val = %d\n",object->val);

                 kmem_cache_free( test_cachep, object );

                 object = NULL;

       }else

            return;

       object = kmem_cache_alloc( test_cachep, GFP_KERNEL );

       if(object)

       {

                 printk("alloc two val = %d\n",object->val);

                 kmem_cache_free( test_cachep, object );

                 object = NULL;

       }else

            return;                

      

}

 

static void fini(void)

{

       printk("test moudle exit ...\n");

       if(test_cachep)

                      kmem_cache_destroy( test_cachep );

}

 

module_init(init);

module_exit(fini);

我们把模块加载之后,用dmesg的命令可以看到如下的输出信息:

slab test moudle init ...

in ctor fuction ...

in ctor fuction ...

……

alloc one val = 1

alloc two val = 1

将模块卸载之后可以看到:

test moudle exit ...

in dtor fuction ...

……

从上我们可以看到,当从cache中分配一个对象时,会初始化很多objectdmesg输出信息中,出现多次in ctor fuction ...),当一个对象释放时,并没有马上调用其析构函数。

我们来看看具体的代码

kmem_cache_create()是创建一个专用cache.同样的,所有专用缓冲区头部也由一个slab分配器维护,它的名字叫:cache_cache。其中每个大个对象的大小均为sizeof(cache).它是静态初始化的:

static kmem_cache_t cache_cache = {

     .lists        = LIST3_INIT(cache_cache.lists),

     .batchcount   = 1,

     .limit        = BOOT_CPUCACHE_ENTRIES,

     .objsize = sizeof(kmem_cache_t),

     .flags        = SLAB_NO_REAP,

     .spinlock = SPIN_LOCK_UNLOCKED,

     .name         = "kmem_cache",

#if DEBUG

     .reallen = sizeof(kmem_cache_t),

#endif

};

Kmem_cache_creat的代码在slab.c中,如下所示:

//参数含义:

//name:cache名字。Align:对齐量.flags:分配标志,ctor:初始化函数 ,dtor析构函数

kmem_cache_t *kmem_cache_create (const char *name, size_t size, size_t align,

     unsigned long flags, void (*ctor)(void*, kmem_cache_t *, unsigned long),

     void (*dtor)(void*, kmem_cache_t *, unsigned long))

{

     size_t left_over, slab_size;

     kmem_cache_t *cachep = NULL;

//参数检测名字不能为空,有析构函数,必须要用初始化函数,不能在中断中,对像不能太大也不能太小(//能超过2^5个页)

     if ((!name) ||in_interrupt() ||(size < BYTES_PER_WORD) ||

         (size > (1<

         (dtor && !ctor)) {

              printk(KERN_ERR "%s: Early error in slab %s\n",

                       __FUNCTION__, name);

              BUG();

         }

 

     if (flags & SLAB_DESTROY_BY_RCU)

         BUG_ON(dtor);

//flag参数的有效性检查

     if (flags & ~CREATE_MASK)

         BUG();

 

     //align参数的调整。如无特别要求,align设为零,flag设为SLAB_HWCACHE_ALIGN。按照处理器缓//存对齐

     if (align) {

         flags &= ~(SLAB_RED_ZONE|SLAB_STORE_USER);

     } else {

         if (flags & SLAB_HWCACHE_ALIGN) {

              //cache_line_size取得处理平始的cache line.前面已经分析过

align = cache_line_size();

//如果对象太小,为了提高利用了,取cache line半数对齐

              while (size <= align/2)

                   align /= 2;

         } else {

              align = BYTES_PER_WORD;

         }

     }

 

//cache_cache中分得一个缓存描述符 kmem_cache_alloc函数在后面讲述

     cachep = (kmem_cache_t *) kmem_cache_alloc(&cache_cache, SLAB_KERNEL);

     if (!cachep)

         goto opps;

//初始化

     memset(cachep, 0, sizeof(kmem_cache_t));

 

     //把大小按照BYTES_PER_WORD 对齐。BYTES_PER_WORD也即处理器的地址单元,在i32 32

     if (size & (BYTES_PER_WORD-1)) {

         size += (BYTES_PER_WORD-1);

         size &= ~(BYTES_PER_WORD-1);

     }

    

//如果size 大于1/8 个页面。就把slab放到缓存区的外面

     if (size >= (PAGE_SIZE>>3))

         flags |= CFLGS_OFF_SLAB;

     //使size按照align对齐

     size = ALIGN(size, align);

 

     if ((flags & SLAB_RECLAIM_ACCOUNT) && size <= PAGE_SIZE) {

         cachep->gfporder = 0;

         cache_estimate(cachep->gfporder, size, align, flags,

                       &left_over, &cachep->num);

     } else {

         //在这里,为cache中每个slab的大小以及slab中的对象个数取得一个平衡点

         do {

              unsigned int break_flag = 0;

cal_wastage:

              //cache_estimate:指定slab的大小后,返回slab中的对像个数

              //以及剩余空间数

              cache_estimate(cachep->gfporder, size, align, flags,

                            &left_over, &cachep->num);

              if (break_flag)

                   break;

              if (cachep->gfporder >= MAX_GFP_ORDER)

                   break;

              if (!cachep->num)

                   goto next;

              if (flags & CFLGS_OFF_SLAB &&

                       cachep->num > offslab_limit) {

                   /* This num of objs will cause problems. */

                   cachep->gfporder--;

                   break_flag++;

                   goto cal_wastage;

              }

 

              /*

               * Large num of objs is good, but v. large slabs are

               * currently bad for the gfp()s.

               */

              if (cachep->gfporder >= slab_break_gfp_order)

                   break;

 

              if ((left_over*8) <= (PAGE_SIZE<gfporder))

                   break;   /* Acceptable internal fragmentation. */

next:

              cachep->gfporder++;

         } while (1);

     }

    

     if (!cachep->num) {

         //出现意外,打印出常现的oops错误

         printk("kmem_cache_create: couldn't create cache %s.\n", name);

         kmem_cache_free(&cache_cache, cachep);

         cachep = NULL;

         goto opps;

     }

     使slab大小按照align对齐

     slab_size = ALIGN(cachep->num*sizeof(kmem_bufctl_t)

                   + sizeof(struct slab), align);

 

    

     if (flags & CFLGS_OFF_SLAB && left_over >= slab_size) {

     //如果剩余空间足间大,就把slab描述符放到缓存区里面

         flags &= ~CFLGS_OFF_SLAB;

         left_over -= slab_size;

     }

 

     if (flags & CFLGS_OFF_SLAB) {

         //如果slab描述符依然只能放到缓存区外面。则取slab_size大小的实际值

         //也就是说不需要与alin 对齐了

         slab_size = cachep->num*sizeof(kmem_bufctl_t)+sizeof(struct slab);

     }

 

//着色偏移量,至少为一个cache_size.align值是自己指定的,且超出了一个cache size.这样//值就会取设定的align

     cachep->colour_off = cache_line_size();

     if (cachep->colour_off < align)

         cachep->colour_off = align;

     //颜色的总数,为剩余的空间数/着色偏移量

     //从这里我们可以看到,如果偏移量太少,着色机制是没有任何意义的

     //这是值得提醒的是colour_next没有被特别赋值,即为默认值0

     cachep->colour = left_over/cachep->colour_off;

     //各种成员的初始化

     cachep->slab_size = slab_size;

     cachep->flags = flags;

     cachep->gfpflags = 0;

     if (flags & SLAB_CACHE_DMA)

         cachep->gfpflags |= GFP_DMA;

     spin_lock_init(&cachep->spinlock);

     cachep->objsize = size;

     /* NUMA */

     INIT_LIST_HEAD(&cachep->lists.slabs_full);

     INIT_LIST_HEAD(&cachep->lists.slabs_partial);

     INIT_LIST_HEAD(&cachep->lists.slabs_free);

    

     //如果slab描述符是放在缓存区外面的。那就为slab描述符指定一个分配缓存

     if (flags & CFLGS_OFF_SLAB)

         cachep->slabp_cache = kmem_find_general_cachep(slab_size,0);

     cachep->ctor = ctor;

     cachep->dtor = dtor;

     cachep->name = name;

 

     /* Don't let CPUs to come and go */

     lock_cpu_hotplug();

 

     //g_cpucache_up:判断普通缓存是否就绪的标志

     //NONE是初始值 PARTIAL:是一个中间的状态,即普通缓存正在初始化

     //FULL:普通缓存已经初始化完成

     if (g_cpucache_up == FULL) {

         enable_cpucache(cachep);

     } else {

         if (g_cpucache_up == NONE) {

              /* Note: the first kmem_cache_create must create

               * the cache that's used by kmalloc(24), otherwise

               * the creation of further caches will BUG().

               */

              cachep->array[smp_processor_id()] =

                       &initarray_generic.cache;

              g_cpucache_up = PARTIAL;

         } else {

              cachep->array[smp_processor_id()] =

                   kmalloc(sizeof(struct arraycache_init),

                       GFP_KERNEL);

         }

         BUG_ON(!ac_data(cachep));

         ac_data(cachep)->avail = 0;

         ac_data(cachep)->limit = BOOT_CPUCACHE_ENTRIES;

         ac_data(cachep)->batchcount = 1;

         ac_data(cachep)->touched = 0;

         cachep->batchcount = 1;

         cachep->limit = BOOT_CPUCACHE_ENTRIES;

         cachep->free_limit = (1+num_online_cpus())*cachep->batchcount

                       + cachep->num;

     }

 

     cachep->lists.next_reap = jiffies + REAPTIMEOUT_LIST3 +

                   ((unsigned long)cachep)%REAPTIMEOUT_LIST3;

 

     //查看是否有相同名字的cache

     down(&cache_chain_sem);

     {

         struct list_head *p;

         mm_segment_t old_fs;

 

         old_fs = get_fs();

         set_fs(KERNEL_DS);

         list_for_each(p, &cache_chain) {

              kmem_cache_t *pc = list_entry(p, kmem_cache_t, next);

              char tmp;

 

              /*

               * This happens when the module gets unloaded and

               * doesn't destroy its slab cache and noone else reuses

               * the vmalloc area of the module. Print a warning.

               */

#ifdef CONFIG_X86_UACCESS_INDIRECT

              if (__direct_get_user(tmp,pc->name)) {

#else

              if (__get_user(tmp,pc->name)) {

#endif

                   printk("SLAB: cache with size %d has lost its "

                            "name\n", pc->objsize);

                   continue;

              }   

              if (!strcmp(pc->name,name)) {

                   printk("kmem_cache_create: duplicate "

                            "cache %s\n",name);

                   up(&cache_chain_sem);

                   unlock_cpu_hotplug();

                   BUG();

              }   

         }

         set_fs(old_fs);

     }

 

     //cache挂至cache_chain

     list_add(&cachep->next, &cache_chain);

     up(&cache_chain_sem);

     unlock_cpu_hotplug();

opps:

     if (!cachep && (flags & SLAB_PANIC))

         panic("kmem_cache_create(): failed to create slab `%s'\n",

              name);

     return cachep;

}

首先我们遇到的问题是第一个鸡与鸡蛋的问题:新建cache描述符是从cache_cache中分配cache描述符,那cache_cache是从何而来呢?cache_cache是静态定义的一个数据结构,只要静态初始化它的成员就可以了。另一个鸡与鸡蛋的问题就是cachearray数组的初始化问题。例如:

cachep->array[smp_processor_id()] =

                   kmalloc(sizeof(struct arraycache_init),

                       GFP_KERNEL);

也就是说从普通缓存中分得空间,那普通缓存区中的arry如何取得空间呢?这也是一个静态定义的数组:initarray_generic.cache。我们以后再详细分析内存各子系统的初始化过程。详情请关注本站更新。

另外,我们也接触到了着色部份的代码。如下所示:

cachep->colour_off = cache_line_size();

         if (cachep->colour_off < align)

         cachep->colour_off = align;

     cachep->colour = left_over/cachep->colour_off;

着色的原理在前面已经分析过了。Colour_off:每一个slab中偏移值。以colour:颜色的总数,即最大的偏移位置,它的大小为剩余大小/偏移值,colour_next初始化为零。

举例说明:

Colour_off = 32  colour = 2; colour_next = 0

第一个slab偏移colour_next* Colour_off = 0*32 = 0 然后colour_next1。即为1

第二个slab偏移colour_next* Colour_off = 1*32 = 32然后colour_next1。即为2

第三个slab偏移colour_next* Colour_off = 2*32 = 64然后colour_next1。即为3,由于colour2。所以,colour_next = 0;

第四个slab偏移colour_next* Colour_off = 0*32 = 0

……

另外:要注意的是slab大小计算的时候:

slab_size = ALIGN(cachep->num*sizeof(kmem_bufctl_t) + sizeof(struct slab), align);

虽然在struct slab里没有定义kmem_bufctl_t.但在为slab申请空间的时候申请了numkmem_bufctl_t的多余空间,也就是说kmem_bufctl_t数组紧放在slab描述符之后

此外,array被初始化了arraycache_init大小。

struct arraycache_init {

     struct array_cache cache;

     void * entries[BOOT_CPUCACHE_ENTRIES];

};

为什么要这样做?我们在后面再给出分析

阅读(3259) | 评论(0) | 转发(4) |
给主人留下些什么吧!~~