分类: LINUX
2011-04-02 14:20:13
在上篇文章中我们大概分析了一下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<
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<
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<
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