Chinaunix首页 | 论坛 | 博客
  • 博客访问: 294148
  • 博文数量: 60
  • 博客积分: 2697
  • 博客等级: 少校
  • 技术积分: 653
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-13 15:52
文章分类

全部博文(60)

文章存档

2012年(6)

2011年(31)

2010年(23)

分类: LINUX

2011-03-15 12:41:19

在上篇文章中我们大概分析了一下kmem_cache_init()这个函数,里面涉及的kmem_cache_create()函数是本片文章分析的主要函数。现在我们来好好看看它的具体代码!

  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))//可以看出函数返回kmem_cache_t结构类型

  {
          size_t left_over, slab_size;
          kmem_cache_t *cachep = NULL;

          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 DEBUG//调试
      WARN_ON(strchr(name, ' '));//如果内存名字中有空格,发出警告。
      if ((flags & SLAB_DEBUG_INITIAL) && !ctor) {//如果设置了SLAB_DEBUG_INITIAL标志,但是没有构造函数,就会出现错误。

                                                                      //这个标志是slab初始化调试位,而初始化工作是由构造函数完成的。
              printk(KERN_ERR "%s: No con, but init state check ""requested - %s\n", __FUNCTION__, name);
              flags &= ~SLAB_DEBUG_INITIAL;//清除这个标志位。
      }

 #if FORCED_DEBUG//强制调试

       if ((size < 4096 || fls(size-1) == fls(size-1+3*BYTES_PER_WORD)))//如果size小于4kb或者两个数量级是一样的。
              flags |= SLAB_RED_ZONE|SLAB_STORE_USER;//设置标志

       if (!(flags & SLAB_DESTROY_BY_RCU))//如果没有设置由rcu释放内存页标志。
              flags |= SLAB_POISON;设置有害对象标志位。

 #endif
      if (flags & SLAB_DESTROY_BY_RCU)//如果设置了由rcu释放内存页标志。
             BUG_ON(flags & SLAB_POISON);//并且如果还设置了有害对象标志位的话,系统崩溃。
 #endif

      if (flags & SLAB_DESTROY_BY_RCU)
             BUG_ON(dtor);//如果设置了。。。同时还有析构函数的话,系统崩溃。

      if (flags & ~CREATE_MASK)
             BUG();//如果设置了高速缓存内存创建时专用标志位之外的标志位,系统则崩溃。

      if (align) {

             flags &= ~(SLAB_RED_ZONE|SLAB_STORE_USER);
      } else {//如果没有设置对齐方式。
             if (flags & SLAB_HWCACHE_ALIGN) {//如果设置了按处理器硬件cache line大小对齐标志。

                  align = cache_line_size();//对齐方式就取处理器的cache line大小。
                  while (size <= align/2)
                            align /= 2;//如果对象大小小于或等于对齐方式的一半,我们就把对齐方式再取一半。
             } else {
                  align = BYTES_PER_WORD;//如果没有设置标志,则我们使对齐方式为系统硬件的字大小对齐。
             }
      }
      cachep = (kmem_cache_t *) kmem_cache_alloc(&cache_cache, SLAB_KERNEL);//从kmem_cache_t结构的高速缓存内

                                                                                   //存中申请一个对象。我们会在后展开讲解的。
      if (!cachep)
         goto opps;//申请失败,我们跳转到opps处。
      memset(cachep, 0, sizeof(kmem_cache_t));//我们将申请到的空间进行清零。

      if (size & (BYTES_PER_WORD-1)) {//要求申请的内存对象的大小至少要按处理器字对齐
         size += (BYTES_PER_WORD-1);
         size &= ~(BYTES_PER_WORD-1);
      }

#if DEBUG
      cachep->reallen = size;

      if (flags & SLAB_RED_ZONE) {

         align = BYTES_PER_WORD;

         cachep->dbghead += BYTES_PER_WORD;
         size += 2*BYTES_PER_WORD;
      }

      if (flags & SLAB_STORE_USER) {

         align = BYTES_PER_WORD;
         size += BYTES_PER_WORD;
     }

#if FORCED_DEBUG && defined(CONFIG_DEBUG_PAGEALLOC)
     if (size > 128 && cachep->reallen > cache_line_size() && size < PAGE_SIZE) {
         cachep->dbghead += PAGE_SIZE - size;
         size = PAGE_SIZE;
    }

#endif
#endif//对于上面的两个if和endif嵌套,由于我只是可以做到对字面的简单理解,具体为什么是这样,我就不太清楚了。

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

       flags |= CFLGS_OFF_SLAB;//如果申请内存对象的大小大于一页的1/8的话,slab管理数据空间就要放在高速缓存内存空间之外。

    size = ALIGN(size, align);//要求申请大小按align对齐。

    if ((flags & SLAB_RECLAIM_ACCOUNT) && size <= PAGE_SIZE) {//设置了统计回收该slab数量,并且size小于一页的大小。

        cachep->gfporder = 0;//由于内存对象小于一页,gfporder是统计每个slab所占的总页数,由于size不超过一页,则页数就

                                       //为2的0次方,就是用1页就可以满足对象空间。
        cache_estimate(cachep->gfporder, size, align, flags,&left_over, &cachep->num);//这个函数上面我们也接触过,就是

                                    //检查一个cache->gfporder这么大空间可以放下多少个内存对象,num就是统计这个slab对象的个数。

    } else {

              do {//这个循环的目的就是找到一个最小的gfporder,使得1<                      unsigned int break_flag = 0;
cal_wastage:
                      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) {//这个if我个人的理解如下:

                                                //offslab_limit这个全局变量记录了malloc_size[]中最大的普通内存对象用来分开存放slab管理数据

                                            //结构中kmem_bufctl_t个数。为什么是malloc_size[]中最大的普通内存对象呢?由于越到后面的成员

                                    //,所表示的申请内存对象大小(cs_size)就越大。所以到最后,offslab_limit记录着是最大。。个数。如果

                                //连这个个数也超过了,就说明装了太多的内存对象。

                          cachep->gfporder--;
                          break_flag++;
                          goto cal_wastage;
                    }

                    if (cachep->gfporder >= slab_break_gfp_order)
                       break;

                   if ((left_over*8) <= (PAGE_SIZE<gfporder))
                      break; //如果空闲的空间是cache->gfdorder的8分之1的话
next:
                   cachep->gfporder++;
              } while (1);
     }

     if (!cachep->num) {//如果没有找到合适的空间来分配内存对象的话
         printk("kmem_cache_create: couldn't create cache %s.\n", name);
         kmem_cache_free(&cache_cache, cachep);//释放原来申请的cachep回cache_cache高速缓存内存
         cachep = NULL;
         goto opps;
    }

    slab_size = ALIGN(cachep->num*sizeof(kmem_bufctl_t)+ sizeof(struct slab), align);//计算高速缓存内存的slab数据管理

                                                                                               //空间大小。

    if (flags & CFLGS_OFF_SLAB && left_over >= slab_size) {
         flags &= ~CFLGS_OFF_SLAB;
         left_over -= slab_size;//如果设置了slab数据管理和缓存对象分开存放的话,同时剩下的空间left_over要比slab_size,就说明

                             //可以用剩余空间,把他们两者放在一起。
   }
   if (flags & CFLGS_OFF_SLAB) {

         slab_size = cachep->num*sizeof(kmem_bufctl_t)+sizeof(struct slab);
  }//关于这里,我不太能理解。这里的slab_size是不对齐的记录slab管理数据空间大小。

  cachep->colour_off = cache_line_size();

  if (cachep->colour_off < align)
        cachep->colour_off = align;//这两个语句是设置色块大小的。

  cachep->colour = left_over/cachep->colour_off;//看看剩余的空间可以存放多少个色块。

  cachep->slab_size = slab_size;//记录该高速缓存内存slab管理数据的大小。

  cachep->gfpflags = 0;//如果高速缓存内存是位于DMA页区中,这个标志就要设置为GFP_DMA标志。如果为0就说明是创建在普通内

                                                                                                                    //存页区中。

  if (flags & SLAB_CACHE_DMA)
       cachep->gfpflags |= GFP_DMA;//这里说明如果要求申请的内存位于DMA中。

  spin_lock_init(&cachep->spinlock);
  cachep->objsize = size;//设置高速缓存内存对象大小。

  INIT_LIST_HEAD(&cachep->lists.slabs_full);
  INIT_LIST_HEAD(&cachep->lists.slabs_partial);
  INIT_LIST_HEAD(&cachep->lists.slabs_free);//初始化slab的三个链表,分别是对象全空闲free,部分空闲partial,没有空闲full

  if (flags & CFLGS_OFF_SLAB)
     cachep->slabp_cache = kmem_find_general_cachep(slab_size,0); //如果slab管理数据空间和对象空间不是放在一起的话,

                                                                                  //我们要在malloc_size[]中找到大小为slab_size的高速缓存内存,然后

                                                                                //然slabp_cache指向它。gfpflags设置了GFP_DMA的话,我们返回指向

                                                                                //位于dma页区的高速缓存内存cs_dmacachep。反之则只是cs_cachep。

 cachep->ctor = ctor;//设置构造函数
 cachep->dtor = dtor;//设置析构函数
 cachep->name = name;//设置高速缓存内存的名字

if (g_cpucache_up == FULL) {//CPU的所有cache都使能了
    enable_cpucache(cachep);//这个函数主要是初始化高速缓存内存阵列
 } else {

           if (g_cpucache_up == NONE) {//如果是没有cache使能的话

               cachep->array[smp_processor_id()] = &initarray_generic.cache;//使当前cpu对应的struct array_cache结构体指

                                                                                            //向initarray_generic.cache。
               g_cpucache_up = PARTIAL;
           } else {如果g_cpucache_up等于PARTIAL
               cachep->array[smp_processor_id()] = kmalloc(sizeof(struct arraycache_init),GFP_KERNEL);//为cachep的当前

                                                 //CPU对应的struct array_cache结构和该cachep的所有高速缓存内存对象指针空间申请内存空间
          }

          BUG_ON(!ac_data(cachep));//如果cachep指针为空,系统则崩溃了。

          ac_data(cachep)->avail = 0;
          ac_data(cachep)->limit = BOOT_CPUCACHE_ENTRIES;
          ac_data(cachep)->batchcount = 1;
          ac_data(cachep)->touched = 0;//以上四句都是对cachep的当前cpu对应的arrary_cache的四个成员赋值。

          cachep->batchcount = 1;
          cachep->limit = BOOT_CPUCACHE_ENTRIES;
          cachep->free_limit = (1+num_online_cpus())*cachep->batchcount+ cachep->num;//高速缓存内存的3个链表空闲总

                                                                                                                    //页数不可以超过这个上限值。
 }

 cachep->lists.next_reap = jiffies + REAPTIMEOUT_LIST3 +((unsigned long)cachep)%REAPTIMEOUT_LIST3;//下次回收时

                                    //间,jiffies是cpu的当前时间,REAPTIMEOUT_LIST3是预定超时时间。

 down(&cache_chain_sem);//获得高速缓存内存链表互斥信号量。

 {
   struct list_head *p;
   mm_segment_t old_fs;

   old_fs = get_fs();//获得当前进程信息结构中的地址限制值current_thread_info->adder_limit

   set_fs(KERNEL_DS);//把当前进程信息结构中的地址限制设置为KERNEL_DS,同时当KERNEL_DS=0时,把内核储存域

   //DOMAIN_KERNEL设置为管理者访问权限(DOMAIN_MANAGER),当KERNEL_DS=1时,设置为用户反问权限。

 list_for_each(p, &cache_chain) {//遍历cache_chain链表中的每个节点

       kmem_cache_t *pc = list_entry(p, kmem_cache_t, next);//通过kmem_cache_t的next成员寻找到对应的高速缓存内存(kmem_cache_t)。
                        char tmp;

                        if (__get_user(tmp,pc->name)) { //读取高速缓存内存名字的第一个字节失败
                             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();//使能cpu热插拔。
                             BUG(); //接着系统崩溃。
                        }

    }
  set_fs(old_fs);//这里是把当前进程信号结构的地址限制值设置为原先的保留值。
 }

 list_add(&cachep->next, &cache_chain);//将创建的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);//如果创建失败并且设置了SLAB_PANIC标志,打印错
 return cachep;
}

 

http://blog.csdn.net/satanwxd/archive/2010/03/16/5384804.aspx

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