免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1059 | 回复: 0
打印 上一主题 下一主题

Linux内存管理之slab分配器分析(续二) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-10-03 20:16 |只看该作者 |倒序浏览
五: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中分配一个对象时,会初始化很多object(dmesg输出信息中,出现多次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
         (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;
         } 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
         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) 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
         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是静态定义的一个数据结构,只要静态初始化它的成员就可以了。另一个鸡与鸡蛋的问题就是cache中array数组的初始化问题。例如:
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
         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_next加1。即为1
第二个slab偏移colour_next* Colour_off = 1*32 = 32然后colour_next加1。即为2
第三个slab偏移colour_next* Colour_off = 2*32 = 64然后colour_next加1。即为3,由于colour为2。所以,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申请空间的时候申请了num个kmem_bufctl_t的多余空间,也就是说kmem_bufctl_t数组紧放在slab描述符之后
此外,array被初始化了arraycache_init大小。
struct arraycache_init {
     struct array_cache cache;
     void * entries[BOOT_CPUCACHE_ENTRIES];
};
为什么要这样做?我们在后面再给出分析
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/86301/showart_2063769.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP