首先说缓存区的数据结构:
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;
我们主要研究on_slab的方法,针对于小对象。看完了数据结构,我们结合图再来理解下slab块。
![](https://img-blog.csdnimg.cn/2022010708530811188.png)
图 1
我们看到上图,有一个kmem_bufctl_t类型数组。起初被赋值为1,2,3,4.....,slab中free字段赋值为0。表示第一个可供分配的是第0个对象,随后free被赋值为kmem_bufctl_t类型数组[0] = 1,表示下次第1个对象是可供分配的。
![](https://img-blog.csdnimg.cn/2022010708530830978.png)
图 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。
整体框图如下:
![](https://img-blog.csdn.net/20150304105407389?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamx0eGdjeQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
图 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->gfporder, size, flags,
&left_over, &cachep->num);//用来计算缓冲区中的一个slab块中可以包含的对象的个数,和slab块中剩余空间
if (break_flag)
break;
if (cachep->gfporder >= MAX_GFP_ORDER)
break;
if (!cachep->num)//N个页面的大小无法放下一个对象
goto next;//再试试更大的空间
if (flags & CFLGS_OFF_SLAB && cachep->num > offslab_limit) {//我们不关心off_slab模式
/* Oops, 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) <= (PAGE_SIZE<<cachep->gfporder))//N个页面的大小至少要可以放下一个对象,且剩余空间不大于整个空间的1/8
break; /* Acceptable internal fragmentation. */
next:
cachep->gfporder++;//试试更大的空间
} while (1);
if (!cachep->num) {//说明N个页面至少可以容纳一个对象
printk("kmem_cache_create: couldn't create cache %s.\n", name);
kmem_cache_free(&cache_cache, cachep);
cachep = NULL;
goto opps;
}
slab_size = L1_CACHE_ALIGN(cachep->num*sizeof(kmem_bufctl_t)+sizeof(slab_t));
/*
* If the slab has been placed off-slab, and we have enough space then
* move it on-slab. This is at the expense of any extra colouring.
*/
if (flags & CFLGS_OFF_SLAB && left_over >= slab_size) {//我们不关心off_slab模式
flags &= ~CFLGS_OFF_SLAB;
left_over -= slab_size;
}
/* Offset must be a multiple of the alignment. */
offset += (align-1);
offset &= ~(align-1);
if (!offset)//offset为0
offset = L1_CACHE_BYTES;
cachep->colour_off = offset;//前面解释了这个变量的用途
cachep->colour = left_over/offset;
/* init remaining fields */
if (!cachep->gfporder && !(flags & CFLGS_OFF_SLAB))
flags |= CFLGS_OPTIMIZE;
cachep->flags = flags;
cachep->gfpflags = 0;
if (flags & SLAB_CACHE_DMA)
cachep->gfpflags |= GFP_DMA;
spin_lock_init(&cachep->spinlock);
cachep->objsize = size;//对象的大小
INIT_LIST_HEAD(&cachep->slabs);
cachep->firstnotfull = &cachep->slabs;//刚开始时fistnotfull指向cachep->slabs
if (flags & CFLGS_OFF_SLAB)
cachep->slabp_cache = kmem_find_general_cachep(slab_size,0);
cachep->ctor = ctor;//构造函数
cachep->dtor = dtor;//析构函数
/* Copy name over so we don't have problems with unloaded modules */
strcpy(cachep->name, name);//名字
......
/* Need the semaphore to access the chain. */
down(&cache_chain_sem);
{
struct list_head *p;
list_for_each(p, &cache_chain) {
kmem_cache_t *pc = list_entry(p, kmem_cache_t, next);
/* The name field is constant - no lock needed. */
if (!strcmp(pc->name, name))//看看是否重复了
BUG();
}
}
/* There is no reason to lock our new cache before we
* link it in - no one knows about it yet...
*/
list_add(&cachep->next, &cache_chain);//如果不重复,就把缓存区加入到cache_chain中,如上图
up(&cache_chain_sem);
opps:
return cachep;
}
其中kmem_cache_estimate,用来计算缓冲区中的一个slab块中可以包含的对象的个数,和slab块中剩余空间。代码如下:
static void kmem_cache_estimate (unsigned long gfporder, size_t size,
int flags, size_t *left_over, unsigned int *num)
{
int i;
size_t wastage = PAGE_SIZE<<gfporder;//2 ^ gfporder个页面的字节数
size_t extra = 0;
size_t base = 0;
if (!(flags & CFLGS_OFF_SLAB)) {
base = sizeof(slab_t);//slab_s数据结构大小
extra = sizeof(kmem_bufctl_t);//kmem_bufctl_t类型数组个数
}
i = 0;
while (i*size + L1_CACHE_ALIGN(base+i*extra) <= wastage)//对象的大小 * 对象个数 + slab_s数据结构大小 + kmem_bufctl_t类型数组个数 * 对象个数
i++;//统计出最合适的对象个数
if (i > 0)
i--;
if (i > SLAB_LIMIT)
i = SLAB_LIMIT;
*num = i;//对象的个数赋值给cachep->num
wastage -= i*size;
wastage -= L1_CACHE_ALIGN(base+i*extra);
*left_over = wastage;//剩余空间赋值给left_over
}
2、分配,kmem_cache_alloc
void * kmem_cache_alloc (kmem_cache_t *cachep, int flags)
{
return __kmem_cache_alloc(cachep, flags);
}
__kmem_cache_alloc如下:
static inline void * __kmem_cache_alloc (kmem_cache_t *cachep, int flags)//cachep就是我们刚刚创建的缓存区
{
unsigned long save_flags;
void* objp;
kmem_cache_alloc_head(cachep, flags);//DEBUG开关开的时候才有意义
try_again:
local_irq_save(save_flags);
#ifdef CONFIG_SMP
......
#else
objp = kmem_cache_alloc_one(cachep);//分配一个对象
#endif
local_irq_restore(save_flags);
return objp;
alloc_new_slab:
......
local_irq_restore(save_flags);
if (kmem_cache_grow(cachep, flags))
/* Someone may have stolen our objs. Doesn't matter, we'll
* just come back here again.
*/
goto try_again;
return NULL;
}
其中cachep就是我们刚刚创建的缓存区。调用kmem_cache_alloc_one在slab块中分配一个对象,如果没有分配到,那么会执行kmem_cache_grow增加一个slab块。
kmem_cache_alloc_one函数代码如下:
#define kmem_cache_alloc_one(cachep) \
({ \
slab_t *slabp; \
\
/* Get slab alloc is to come from. */ \
{ \
struct list_head* p = cachep->firstnotfull; \
if (p == &cachep->slabs) \ //如果fistnotfull指向cachep->slabs,那么要增加一个slab块
goto alloc_new_slab; \
slabp = list_entry(p,slab_t, list); \
} \
kmem_cache_alloc_one_tail(cachep, slabp); \
})
目前firstnotfull指向cachep->slabs,goto alloc_new_slab,执行
kmem_cache_grow,代码如下:
static int kmem_cache_grow (kmem_cache_t * cachep, int flags)
{
slab_t *slabp;
struct page *page;
void *objp;
size_t offset;
unsigned int i, local_flags;
unsigned long ctor_flags;
unsigned long save_flags;
/* Be lazy and only check for valid flags here,
* keeping it out of the critical path in kmem_cache_alloc().
*/
if (flags & ~(SLAB_DMA|SLAB_LEVEL_MASK|SLAB_NO_GROW))
BUG();
if (flags & SLAB_NO_GROW)
return 0;
......
if (in_interrupt() && (flags & SLAB_LEVEL_MASK) != SLAB_ATOMIC)
BUG();
ctor_flags = SLAB_CTOR_CONSTRUCTOR;
local_flags = (flags & SLAB_LEVEL_MASK);
if (local_flags == SLAB_ATOMIC)
......
ctor_flags |= SLAB_CTOR_ATOMIC;
/* About to mess with non-constant members - lock. */
spin_lock_irqsave(&cachep->spinlock, save_flags);
/* Get colour for the slab, and cal the next value. */
offset = cachep->colour_next;//目前为0
cachep->colour_next++;//下一次为1
if (cachep->colour_next >= cachep->colour)//当达到最大值时,colour_next清零
cachep->colour_next = 0;
offset *= cachep->colour_off;//当前偏移为0
cachep->dflags |= DFLGS_GROWN;
cachep->growing++;//缓存区正在增长中
spin_unlock_irqrestore(&cachep->spinlock, save_flags);
......
/* Get mem for the objs. */
if (!(objp = kmem_getpages(cachep, flags)))//获取slab块对应大小的空间(2 ^ gfporder个页面)
goto failed;
/* Get slab management. */
if (!(slabp = kmem_cache_slabmgmt(cachep, objp, offset, local_flags)))//初始化slab块中slab_s数据结构
goto opps1;
/* Nasty!!!!!! I hope this is OK. */
i = 1 << cachep->gfporder;//页面数
page = virt_to_page(objp);
do {
SET_PAGE_CACHE(page, cachep);//page结构中的list的next域赋值为指向它所在的缓存区的kmem_cache_t类型描述结构的指针
SET_PAGE_SLAB(page, slabp);//page结构中的list的next域赋值为指向它所在的slab的slab_t类型描述结构的指针
PageSetSlab(page);
page++;
} while (--i);
kmem_cache_init_objs(cachep, slabp, ctor_flags);//设置slab块中kmem_bufctl_t类型数组和对象的初始化
spin_lock_irqsave(&cachep->spinlock, save_flags);
cachep->growing--;//缓存区已经停止生长
/* Make slab active. */
list_add_tail(&slabp->list,&cachep->slabs);//将slabp链入cachep->slabs末尾,如图3,不过现在只有空闲块
if (cachep->firstnotfull == &cachep->slabs)
cachep->firstnotfull = &slabp->list;//将firstnotfull指向刚刚的空闲slab块
STATS_INC_GROWN(cachep);
cachep->failures = 0;
spin_unlock_irqrestore(&cachep->spinlock, save_flags);
return 1;
opps1:
kmem_freepages(cachep, objp);
failed:
spin_lock_irqsave(&cachep->spinlock, save_flags);
cachep->growing--;
spin_unlock_irqrestore(&cachep->spinlock, save_flags);
return 0;
}
kmem_getpages用来获取slab块对应大小的空间(2 ^ gfporder个页面),代码如下:
static inline void * kmem_getpages (kmem_cache_t *cachep, unsigned long flags)
{
void *addr;
/*
* If we requested dmaable memory, we will get it. Even if we
* did not request dmaable memory, we might get it, but that
* would be relatively rare and ignorable.
*/
flags |= cachep->gfpflags;
addr = (void*) __get_free_pages(flags, cachep->gfporder);//获取
/* Assume that now we have the pages no one else can legally
* messes with the 'struct page's.
* However vm_scan() might try to test the structure to see if
* it is a named-page or buffer-page. The members it tests are
* of no interest here.....
*/
return addr;
}
kmem_cache_slabmgmt用来初始化slab块中slab_s数据结构,代码如下:
static inline slab_t * kmem_cache_slabmgmt (kmem_cache_t *cachep,
void *objp, int colour_off, int local_flags)
{
slab_t *slabp;
if (OFF_SLAB(cachep)) {
/* Slab management obj is off-slab. */
slabp = kmem_cache_alloc(cachep->slabp_cache, local_flags);
if (!slabp)
return NULL;
} else {
/* FIXME: change to
slabp = objp
* if you enable OPTIMIZE
*/
slabp = objp+colour_off;//目前colour_off为0,slab块中slab_s数据结构的位置确定了
colour_off += L1_CACHE_ALIGN(cachep->num *
sizeof(kmem_bufctl_t) + sizeof(slab_t));//当前slab块中的第一个对象距离slab块所在页面的页面边界位置的偏移量
}
slabp->inuse = 0;//活跃对象数量是0
slabp->colouroff = colour_off;
slabp->s_mem = objp+colour_off;//第一个对象的内存地址
return slabp;
}
SET_PAGE_CACHE(page, cachep),page结构中的list的next域赋值为指向它所在的缓存区的kmem_cache_t类型描述结构的指针,代码如下:
#define SET_PAGE_CACHE(pg,x) ((pg)->list.next = (struct list_head *)(x))
SET_PAGE_SLAB(page, slabp),page结构中的list的next域赋值为指向它所在的slab的slab_t类型描述结构的指针,代码如下:
#define SET_PAGE_SLAB(pg,x) ((pg)->list.prev = (struct list_head *)(x))
PageSetSlab,设置页的flags,代码如下:
#define PageSetSlab(page) set_bit(PG_slab, &(page)->flags)
kmem_cache_init_objs,设置slab块中kmem_bufctl_t类型数组和对象的初始化,代码如下:
static inline void kmem_cache_init_objs (kmem_cache_t * cachep,
slab_t * slabp, unsigned long ctor_flags)
{
int i;
for (i = 0; i < cachep->num; i++) {
void* objp = slabp->s_mem+cachep->objsize*i;
......
if (cachep->ctor)
cachep->ctor(objp, cachep, ctor_flags);//用构造函数构造所有对象
......
slab_bufctl(slabp)[i] = i+1;//形成的格局是图2,数组中元素是1,2,3,4....
}
slab_bufctl(slabp)[i-1] = BUFCTL_END;//比如有5个对象,那么第5个元素依次是1,2,3,4,BUFCTL_END
slabp->free = 0;//表示第0个对象是可供分配的
}
回到__kmem_cache_alloc,kmem_cache_grow返回1,goto try_again,kmem_cache_alloc_one分配对象,代码如下:
#define kmem_cache_alloc_one(cachep) \
({ \
slab_t *slabp; \
\
/* Get slab alloc is to come from. */ \
{ \
struct list_head* p = cachep->firstnotfull; \
if (p == &cachep->slabs) \ //此时firstnotfull指向刚刚的空闲slab块
goto alloc_new_slab; \
slabp = list_entry(p,slab_t, list); \ //空闲的slab块
} \
kmem_cache_alloc_one_tail(cachep, slabp); \
})
kmem_cache_alloc_one_tail,代码如下:
static inline void * kmem_cache_alloc_one_tail (kmem_cache_t *cachep,
slab_t *slabp)
{
void *objp;
STATS_INC_ALLOCED(cachep);
STATS_INC_ACTIVE(cachep);
STATS_SET_HIGH(cachep);
/* get obj pointer */
slabp->inuse++;//被分配了的对象数量加1
objp = slabp->s_mem + slabp->free*cachep->objsize;//被分配对象的地址,目前分配的是第一个对象,slabp->free为0
slabp->free=slab_bufctl(slabp)[slabp->free];//slabp->free为1
if (slabp->free == BUFCTL_END)
/* slab now full: move to next slab for next alloc */
cachep->firstnotfull = slabp->list.next;//如果当前slab没有了空闲对象,那么firstnotfull执行下一个slab块
......
return objp;
}
不断调用kmem_cache_alloc,直到分配到三个完全块,一个部分块为至,假设完全块有5个对象,部分块现在只有两个对象,这是图3,最初期的样子,随着对象的释放,会逐渐变成图 3的样子。
3、释放,kmem_cache_free
(1)、我们首先释放第4个部分块的对象,上面我们说过部分块一共两个对象,我们首先释放第2个对象。
void kmem_cache_free (kmem_cache_t *cachep, void *objp)//objp为要释放对象的地址,cachep就是我们刚刚创建的缓存区
{
unsigned long flags;
......
local_irq_save(flags);
__kmem_cache_free(cachep, objp);
local_irq_restore(flags);
}
__kmem_cache_free,代码如下:
static inline void __kmem_cache_free (kmem_cache_t *cachep, void* objp)
{
......
kmem_cache_free_one(cachep, objp);
......
}
kmem_cache_free_one,代码如下:
现在kmem_bufctl_t类型数组,元素是1,2,3,4,BUFCTL_END,slabp->free为2。
static inline void kmem_cache_free_one(kmem_cache_t *cachep, void *objp)
{
slab_t* slabp;
CHECK_PAGE(virt_to_page(objp));
/* reduces memory footprint
*
if (OPTIMIZE(cachep))
slabp = (void*)((unsigned long)objp&(~(PAGE_SIZE-1)));
else
*/
slabp = GET_PAGE_SLAB(virt_to_page(objp));//得到这个对象所在slab块的描述结构指针
......
{
unsigned int objnr = (objp - slabp->s_mem)/cachep->objsize;//得到第2个对象在slab块内的对象数组中的下标,为1
slab_bufctl(slabp)[objnr] = slabp->free;//现在kmem_bufctl_t类型数组,元素是1,2,3,4,BUFCTL_END,不变
slabp->free = objnr;//free为1
}
STATS_DEC_ACTIVE(cachep);
/* fixup slab chain */
if (slabp->inuse-- == cachep->num)//不是由完全块变成部分块
goto moveslab_partial;
if (!slabp->inuse)//不是由部分块变成完全块
goto moveslab_free;
return;
moveslab_partial:
......
{
struct list_head *t = cachep->firstnotfull;
cachep->firstnotfull = &slabp->list;
if (slabp->list.next == t)
return;
list_del(&slabp->list);
list_add_tail(&slabp->list, t);
return;
}
moveslab_free:
......
{
struct list_head *t = cachep->firstnotfull->prev;
list_del(&slabp->list);
list_add_tail(&slabp->list, &cachep->slabs);
if (cachep->firstnotfull == &slabp->list)
cachep->firstnotfull = t->next;
return;
}
}
virt_to_page,代码如下:
#define virt_to_page(kaddr) (mem_map + (__pa(kaddr) >> PAGE_SHIFT))
GET_PAGE_SLAB,得到这个对象所在slab块的描述结构指针,代码如下:
#define GET_PAGE_SLAB(pg) ((slab_t *)(pg)->list.prev)
(2)、接着,释放第4个部分块的对象,上面我们说过部分块一共两个对象,刚才释放了第2个对象,我们再释放第1个对象。
kmem_cache_free_one,代码如下:
现在kmem_bufctl_t类型数组,元素是1,2,3,4,BUFCTL_END,slabp->free为1。
static inline void kmem_cache_free_one(kmem_cache_t *cachep, void *objp)
{
slab_t* slabp;
CHECK_PAGE(virt_to_page(objp));
/* reduces memory footprint
*
if (OPTIMIZE(cachep))
slabp = (void*)((unsigned long)objp&(~(PAGE_SIZE-1)));
else
*/
slabp = GET_PAGE_SLAB(virt_to_page(objp));//得到这个对象所在slab块的描述结构指针
......
{
unsigned int objnr = (objp - slabp->s_mem)/cachep->objsize;//得到第1个对象在slab块内的对象数组中的下标,为0
slab_bufctl(slabp)[objnr] = slabp->free;//现在kmem_bufctl_t类型数组,元素是1,2,3,4,BUFCTL_END,不变
slabp->free = objnr;//free为0
}
STATS_DEC_ACTIVE(cachep);
/* fixup slab chain */
if (slabp->inuse-- == cachep->num)//不是由完全块变成部分块
goto moveslab_partial;
if (!slabp->inuse)//是由部分块变成空闲块
goto moveslab_free;//转到moveslab_free
return;
moveslab_partial:
......
{
struct list_head *t = cachep->firstnotfull;
cachep->firstnotfull = &slabp->list;
if (slabp->list.next == t)
return;
list_del(&slabp->list);
list_add_tail(&slabp->list, t);
return;
}
moveslab_free:
......
{
struct list_head *t = cachep->firstnotfull->prev;//指向前面的第三个完全块
list_del(&slabp->list);
list_add_tail(&slabp->list, &cachep->slabs);//将这个空闲块挪到cachep->slabs末尾
if (cachep->firstnotfull == &slabp->list)//如果当前firstnotfull指向了刚才的空闲块
cachep->firstnotfull = t->next;//firstnotfull指向第三个完全块的下一个块,其实还是刚才的空闲块
return;
}
}
第4个部分块已经变成了空闲块
(3)、我们开始释放第3个完全块的对象,上面我们说过完全块一共5个对象,我们首先释放第2个对象。
kmem_cache_free_one,代码如下:
现在kmem_bufctl_t类型数组,元素是1,2,3,4,BUFCTL_END,slabp->free为BUFCTL_END。
static inline void kmem_cache_free_one(kmem_cache_t *cachep, void *objp)
{
slab_t* slabp;
CHECK_PAGE(virt_to_page(objp));
/* reduces memory footprint
*
if (OPTIMIZE(cachep))
slabp = (void*)((unsigned long)objp&(~(PAGE_SIZE-1)));
else
*/
slabp = GET_PAGE_SLAB(virt_to_page(objp));//得到这个对象所在slab块的描述结构指针
......
{
unsigned int objnr = (objp - slabp->s_mem)/cachep->objsize;//得到第2个对象在slab块内的对象数组中的下标,为1
slab_bufctl(slabp)[objnr] = slabp->free;//现在kmem_bufctl_t类型数组,元素是1,BUFCTL_END,3,4,BUFCTL_END
slabp->free = objnr;//free为1
}
STATS_DEC_ACTIVE(cachep);
/* fixup slab chain */
if (slabp->inuse-- == cachep->num)//是由完全块变成部分块
goto moveslab_partial;//执行这里
if (!slabp->inuse)//不是由部分块变成空闲块
goto moveslab_free;
return;
moveslab_partial:
......
{
struct list_head *t = cachep->firstnotfull;//原来指向第4个空闲块
cachep->firstnotfull = &slabp->list;//fistnotfull指向第三个部分块
if (slabp->list.next == t)//第三个部分块的下一个 等于 第4个空闲块,return
return;
list_del(&slabp->list);
list_add_tail(&slabp->list, t);
return;
}
moveslab_free:
......
{
struct list_head *t = cachep->firstnotfull->prev;
list_del(&slabp->list);
list_add_tail(&slabp->list, &cachep->slabs);
if (cachep->firstnotfull == &slabp->list)
cachep->firstnotfull = t->next;
return;
}
}
第3个完全块,已经变成了部分块。
(4)、接着,我们释放第3个部分块的对象,上面已经释放了第2个对象,现在我们释放第4个对象。
kmem_cache_free_one,代码如下:
现在kmem_bufctl_t类型数组,元素是1,BUFCTL_END,3,4,BUFCTL_END,slabp->free为1。
static inline void kmem_cache_free_one(kmem_cache_t *cachep, void *objp)
{
slab_t* slabp;
CHECK_PAGE(virt_to_page(objp));
/* reduces memory footprint
*
if (OPTIMIZE(cachep))
slabp = (void*)((unsigned long)objp&(~(PAGE_SIZE-1)));
else
*/
slabp = GET_PAGE_SLAB(virt_to_page(objp));//得到这个对象所在slab块的描述结构指针
......
{
unsigned int objnr = (objp - slabp->s_mem)/cachep->objsize;//得到第4个对象在slab块内的对象数组中的下标,为3
slab_bufctl(slabp)[objnr] = slabp->free;//现在kmem_bufctl_t类型数组,元素为1,BUFCTL_END,3,1,BUFCTL_END
slabp->free = objnr;//free为3
}
STATS_DEC_ACTIVE(cachep);
/* fixup slab chain */
if (slabp->inuse-- == cachep->num)//不是由完全块变成部分块
goto moveslab_partial;
if (!slabp->inuse)//不是由部分块变成空闲块
goto moveslab_free;
return;
moveslab_partial:
......
{
struct list_head *t = cachep->firstnotfull;
cachep->firstnotfull = &slabp->list;
if (slabp->list.next == t)
return;
list_del(&slabp->list);
list_add_tail(&slabp->list, t);
return;
}
moveslab_free:
......
{
struct list_head *t = cachep->firstnotfull->prev;
list_del(&slabp->list);
list_add_tail(&slabp->list, &cachep->slabs);
if (cachep->firstnotfull == &slabp->list)
cachep->firstnotfull = t->next;
return;
}
}
这样就形成了图 3的布局,两个完全块,一个部分块,最后一个是空闲块。
下次,在分配对象时,从部分块开始分配,free为3,首先分配第4个对象;然后free赋值为1(kmem_bufctl_t类型数组[4] 为 1),分配第二个对象;free赋值为BUFCTL_END((kmem_bufctl_t类型数组[1] 为 BUFCTL_END)),已经没有可供分配的对象了。这时开始在空闲块中分配,相关代码为:
static inline void * kmem_cache_alloc_one_tail (kmem_cache_t *cachep,
slab_t *slabp)
{
void *objp;
STATS_INC_ALLOCED(cachep);
STATS_INC_ACTIVE(cachep);
STATS_SET_HIGH(cachep);
/* get obj pointer */
slabp->inuse++;
objp = slabp->s_mem + slabp->free*cachep->objsize;
slabp->free=slab_bufctl(slabp)[slabp->free];
if (slabp->free == BUFCTL_END)
/* slab now full: move to next slab for next alloc */
cachep->firstnotfull = slabp->list.next;//指向第4个空闲块
......
return objp;
}
5、kmalloc,分配指定大小的对象,代码如下:
void * kmalloc (size_t size, int flags)
{
cache_sizes_t *csizep = cache_sizes;//如下图
for (; csizep->cs_size; csizep++) {
if (size > csizep->cs_size)//发现第一个缓存区链表中slab块的大小大于或等于申请空间大小的那个数组项
continue;
return __kmem_cache_alloc(flags & GFP_DMA ?
csizep->cs_dmacachep : csizep->cs_cachep, flags);//这个函数,我们上面讲过,返回对象的地址
}
BUG(); // too big size
return NULL;
}
cache_sizes结构如下:
typedef struct cache_sizes {
size_t cs_size;
kmem_cache_t *cs_cachep;
kmem_cache_t *cs_dmacachep;
} cache_sizes_t;
kfree,释放指定的对象代码如下:
void kfree (const void *objp)//objp对象的地址
{
kmem_cache_t *c;
unsigned long flags;
if (!objp)
return;
local_irq_save(flags);
CHECK_PAGE(virt_to_page(objp));
c = GET_PAGE_CACHE(virt_to_page(objp));//获得这个对象所在的缓存区的kmem_cache_t类型的描述变量的指针
__kmem_cache_free(c, (void*)objp);//这个函数我们上面已经讲过了
local_irq_restore(flags);
}
virt_to_page,代码如下:
#define virt_to_page(kaddr) (mem_map + (__pa(kaddr) >> PAGE_SHIFT))
GET_PAGE_CACHE,代码如下:
#define GET_PAGE_CACHE(pg) ((kmem_cache_t *)(pg)->list.next)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)