首先说缓存区的数据结构:
struct kmem_cache_s {
/* 1) each alloc & free */
/* full, partial first, then free */
struct list_head slabs;//指向所有的slab块链表,前面是完全块,然后是非完全块,最后是空闲块
struct list_head *firstnotfull;//指向第一个非完全块,如果没有非完全块,就指向上面的slabs
unsigned int objsize;//对象的大小
unsigned int flags; /* constant flags */
unsigned int num; //对象的数量
spinlock_t spinlock;
#ifdef CONFIG_SMP
unsigned int batchcount;
#endif
/* 2) slab additions /removals */
/* order of pgs per slab (2^n) */
unsigned int gfporder;//对象的页面数2 ^ gfporder
/* force GFP flags, e.g. GFP_DMA */
unsigned int gfpflags;
size_t colour; /* cache colouring range */
unsigned int colour_off; /* colour offset */
unsigned int colour_next; /* cache colouring */
kmem_cache_t *slabp_cache;
unsigned int growing;
unsigned int dflags; /* dynamic flags */
/* constructor func */
void (*ctor)(void *, kmem_cache_t *, unsigned long);//构造函数
/* de-constructor func */
void (*dtor)(void *, kmem_cache_t *, unsigned long);//析构函数
unsigned long failures;
/* 3) cache creation/removal */
char name[CACHE_NAMELEN];//名字
struct list_head next;//链接下一个缓冲区
}
再说slab块的数据结构:
typedef struct slab_s {
struct list_head list; //链接下个slab块
unsigned long colouroff; //当前slab块中的第一个对象距离slab块所在页面的页面边界位置的偏移量
void *s_mem; //指向此slab块中的第一个对象的指针,等于页面边界位置+colouroff
unsigned int inuse; //被使用的对象的数目
kmem_bufctl_t free; //当前第一个空闲对象在此数组中的下标
} slab_t;
图 1
我们看到上图,有一个kmem_bufctl_t类型数组。起初被赋值为1,2,3,4.....,slab中free字段赋值为0。表示第一个可供分配的是第0个对象,随后free被赋值为kmem_bufctl_t类型数组[0] = 1,表示下次第1个对象是可供分配的。
图 2
刚才缓冲区的colour_next ,colour_off,colour我们都没有解释,这里结合图2解释一下。其中页面偏移量实际上是缓存区的colour_next * colour_off得到的。colour_next 初始化为0,当colour_next达到colour时,会被重新置0。colour_off一般被赋值为L1_CACHE_BYTES,colour被赋值为slab块剩余空间/colour_off。
整体框图如下:
图 3
完全slab块在前面,非完全或者说部分slab块放在中间,空闲slab块放到最后。通过kmem_cache_s的slabs,和slab_s的list链接成双向链表。kmem_cache_s的firstnotfull指向第一个非完全slab块。
每个slab块都是 2 ^ gfporder个页面。
下面我结合一个例子,来分析slab块的分配与释放
1、创建缓存区,kmem_cache_create
kmem_cache_t *
kmem_cache_create (const char *name, size_t size, size_t offset,
unsigned long flags, void (*ctor)(void*, kmem_cache_t *, unsigned long),
void (*dtor)(void*, kmem_cache_t *, unsigned long))
{
const char *func_nm = KERN_ERR "kmem_create: ";
size_t left_over, align, slab_size;
kmem_cache_t *cachep = NULL;
/*
* Sanity checks... these are all serious usage bugs.
*/
if ((!name) ||
((strlen(name) >= CACHE_NAMELEN - 1)) ||
in_interrupt() ||
(size < BYTES_PER_WORD) ||
(size > (1<<MAX_OBJ_ORDER)*PAGE_SIZE) ||
(dtor && !ctor) ||
(offset < 0 || offset > size))
BUG();
......
......
if (flags & ~CREATE_MASK)
BUG();
/* Get cache's description obj. */
cachep = (kmem_cache_t *) kmem_cache_alloc(&cache_cache, SLAB_KERNEL);//分配缓存区
if (!cachep)
goto opps;
memset(cachep, 0, sizeof(kmem_cache_t));//将缓存区清0
......
if (size & (BYTES_PER_WORD-1)) {//将size进行字对齐
size += (BYTES_PER_WORD-1);
size &= ~(BYTES_PER_WORD-1);
printk("%sForcing size word alignment - %s\n", func_nm, name);
}
......
align = BYTES_PER_WORD;
if (flags & SLAB_HWCACHE_ALIGN)//调整align
align = L1_CACHE_BYTES;
/* Determine if the slab management is 'on' or 'off' slab. */
if (size >= (PAGE_SIZE>>3))
.......
flags |= CFLGS_OFF_SLAB;//我们假定现在是on_slab模式
if (flags & SLAB_HWCACHE_ALIGN) {
......
while (size < align/2)
align /= 2;
size = (size+align-1)&(~(align-1));//调整size
}
......
do {
unsigned int break_flag = 0;
cal_wastage:
kmem_cache_estimate(cachep-&g