Slab函数分析之kmem_cache_create函数

该函数用于创建Slab缓存。
参数介绍:
name: 缓存名字
size: 对象的大小
align:对齐
flag:Slab标志位
ctor:对象构造函数
dtor:对象析构函数

struct kmem_cache *
kmem_cache_create (const char *name, size_t size, size_t align,
 unsigned long flags, void (*ctor)(void*, struct kmem_cache *, unsigned long),
 void (*dtor)(void*, struct kmem_cache *, unsigned long))
{
 size_t left_over, slab_size, ralign;
 struct kmem_cache *cachep = NULL;
 struct list_head *p;

  对函数使用的检查,下面一种情况发生都将导致死机
  1.name为null
  2.处于中断上下文中
  3.对象大小小于4个字节或者大于32个页面
  4.构造函数存在,析构函数不存在
 if ((!name) ||
     in_interrupt() ||
     (size < BYTES_PER_WORD) ||
     (size > (1 << MAX_OBJ_ORDER) * PAGE_SIZE) || (dtor && !ctor)) {
  printk(KERN_ERR "%s: Early error in slab %s/n",
         __FUNCTION__, name);
  BUG();
 }
 
 锁住CPU热插拔功能
 lock_cpu_hotplug();

  为cache_chain加互斥锁
 mutex_lock(&cache_chain_mutex);

  遍历cache_chain链表
 list_for_each(p, &cache_chain) {
  struct kmem_cache *pc = list_entry(p, struct kmem_cache, next);
  
  由于__get_user函数只在用户环境中执行,因此我们要
  改变fs寄存器,在内核空间也能调用该函数
  mm_segment_t old_fs = get_fs();
  char tmp;
  int res;
   
    设置fs段的内容为内核数据段
  set_fs(KERNEL_DS);
  res = __get_user(tmp, pc->name);
  还原fs段
  set_fs(old_fs);
  if (res) {
   printk("SLAB: cache with size %d has lost its name/n",
          pc->buffer_size);
   continue;
  }
    判断是不是为name的cache是不是已经存在
  if (!strcmp(pc->name, name)) {
   printk("kmem_cache_create: duplicate cache %s/n", name);
   dump_stack();
   goto oops;
  }
 }

  如果采用2.6提供的新的锁机制,read-copy-update来对其释放,
  检查析构函数是不是为空。
 if (flags & SLAB_DESTROY_BY_RCU)
  BUG_ON(dtor);

 检查创建时相应的标志位是不是设置好了。
 CREATE_MASK: (# define CREATE_MASK (SLAB_HWCACHE_ALIGN | SLAB_NO_REAP | /
    SLAB_CACHE_DMA | SLAB_MUST_HWCACHE_ALIGN | /
    SLAB_RECLAIM_ACCOUNT | SLAB_PANIC | /
    SLAB_DESTROY_BY_RCU))
   
 if (flags & ~CREATE_MASK)
  BUG();

  检查对象的大小是不是32位对齐,如果不是
  则进行调整
 if (size & (BYTES_PER_WORD - 1)) {
  size += (BYTES_PER_WORD - 1);
  size &= ~(BYTES_PER_WORD - 1);
 }

  检查Slab是不是按照硬件缓冲行对齐
 if (flags & SLAB_HWCACHE_ALIGN) {
   取硬件缓冲行大小
  ralign = cache_line_size();
  进行对齐大小的调整,直到对象大小大于对齐的大小
  while (size <= ralign / 2)
   ralign /= 2;
 } else {
   默认情况下,对齐大小为4个字节
  ralign = BYTES_PER_WORD;
 }

  对align进行重新调整,确定最终的align值
 if (ralign < ARCH_SLAB_MINALIGN) {
  ralign = ARCH_SLAB_MINALIGN;
  if (ralign > BYTES_PER_WORD)
   flags &= ~(SLAB_RED_ZONE | SLAB_STORE_USER);
 }

 if (ralign < align) {
  ralign = align;
  if (ralign > BYTES_PER_WORD)
   flags &= ~(SLAB_RED_ZONE | SLAB_STORE_USER);
 }
 align = ralign;

  从cache_cache中划分一个新的kmem_cache节点。
 cachep = kmem_cache_alloc(&cache_cache, SLAB_KERNEL);
 if (!cachep)
  goto oops;
 初始化得到的内存
 memset(cachep, 0, sizeof(struct kmem_cache));

  如果发现对象大小是大于512字节,那么Slab缓存的管理区部分应该和
  对象分开存放
 if (size >= (PAGE_SIZE >> 3))
  flags |= CFLGS_OFF_SLAB;

  对齐
 size = ALIGN(size, align);

  计算存放大小为size的对象的order值,以及可以存放的对象数量。返回Slab缓存的
  碎片大小。
 left_over = calculate_slab_order(cachep, size, align, flags);

  如果发现对象数量为0,表示不能创建
 if (!cachep->num) {
  printk("kmem_cache_create: couldn't create cache %s./n", name);
  kmem_cache_free(&cache_cache, cachep);
  cachep = NULL;
  goto oops;
 }
 计算Slab缓存的管理区的大小:slab结构的大小+每个对象有一个控制域的大小(kmem_bufctl_t)
 slab_size = ALIGN(cachep->num * sizeof(kmem_bufctl_t)
     + sizeof(struct slab), align);

 如果Slab管理区是和对象分开存放的,并且Slab中的碎片大小足够存放
 管理区。则需要重新调整Slab管理区的存放位置,可以和Slab对象一起存放。
 if (flags & CFLGS_OFF_SLAB && left_over >= slab_size) {
  flags &= ~CFLGS_OFF_SLAB;
  left_over -= slab_size;
 }

  如果Slab管理区和对象分开存放,Slab管理区的大小为:slab结构的大小+每个对象有一个控制域的大小(kmem_bufctl_t)
 if (flags & CFLGS_OFF_SLAB) {
  slab_size =
      cachep->num * sizeof(kmem_bufctl_t) + sizeof(struct slab);
 }

  计算着色偏移区:初始值为硬件缓存行大小
 cachep->colour_off = cache_line_size();
 调整着色偏移区
 if (cachep->colour_off < align)
  cachep->colour_off = align;
 计算颜色大小:Slab碎片大小除以着色偏移区
 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);
 buffer_size为一个对象的大小
 cachep->buffer_size = size;

  如果对象和管理区分开存放,使用slabp_cache指针来指向管理区的内存,从通用缓存中申请。
 if (flags & CFLGS_OFF_SLAB)
  cachep->slabp_cache = kmem_find_general_cachep(slab_size, 0u);
 cachep->ctor = ctor;
 cachep->dtor = dtor;
 cachep->name = name;

  接下来是对kmem_list3/array cache的一个初始化。每个CPU对应一个array cache
  系统先是分配array cache中的对象,目的是减少对Slab cache的访问次数来
  提高系统性能。
 if (g_cpucache_up == FULL) {
  enable_cpucache(cachep);
 } else {
  if (g_cpucache_up == NONE) {
    为array cache和list3分配内存
   cachep->array[smp_processor_id()] =
       &initarray_generic.cache;
   set_up_list3s(cachep, SIZE_AC);
   if (INDEX_AC == INDEX_L3)
    g_cpucache_up = PARTIAL_L3;
   else
    g_cpucache_up = PARTIAL_AC;
  } else {
    为array cache和list3分配内存
   cachep->array[smp_processor_id()] =
       kmalloc(sizeof(struct arraycache_init), GFP_KERNEL);

   if (g_cpucache_up == PARTIAL_AC) {
    set_up_list3s(cachep, SIZE_L3);
    g_cpucache_up = PARTIAL_L3;
   } else {
    int node;
    初始化kmem_list3
    for_each_online_node(node) {
     cachep->nodelists[node] =
         kmalloc_node(sizeof
        (struct kmem_list3),
        GFP_KERNEL, node);
     BUG_ON(!cachep->nodelists[node]);
     kmem_list3_init(cachep->
       nodelists[node]);
    }
   }
  }
  cachep->nodelists[numa_node_id()]->next_reap =
      jiffies + REAPTIMEOUT_LIST3 +
      ((unsigned long)cachep) % REAPTIMEOUT_LIST3;

  初始化array cache
  cpu_cache_get(cachep)->avail = 0;
  cpu_cache_get(cachep)->limit = BOOT_CPUCACHE_ENTRIES;
  cpu_cache_get(cachep)->batchcount = 1;
  cpu_cache_get(cachep)->touched = 0;
  cachep->batchcount = 1;
  cachep->limit = BOOT_CPUCACHE_ENTRIES;
 }
  将创建的新的kmem_cache连入链表cache_cache中。
 list_add(&cachep->next, &cache_chain);
      oops:
 if (!cachep && (flags & SLAB_PANIC))
  panic("kmem_cache_create(): failed to create slab `%s'/n",
        name);
 mutex_unlock(&cache_chain_mutex);
 unlock_cpu_hotplug();
 return cachep;
}

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值