内核的运行肯定是要对数据进行操作,这就包括数据的分配和释放,那有些数据呢!是内核经常要获取&释放的,如果每次都到内存中去拿再释放回内存,太浪费时间。那这样就需要一种空闲链表,这个链表是一直都在,随时可以进行获取。好吧!内核不是需要数据嘛!来空闲链表中拿一个你需要的那类数据结构,内核使完了就撒手放回到空闲链表中,而不是释放回内存。那这个空闲链表存在那些问题呢?
LKD上说最主要的就是这些链表不能全局控制。什么叫全局控制?如果能全局控制又能怎么样?Og在看到这里的时候就开始有点不适的感觉,其实就是内核不知道有这个叫空闲链表的东西,所以呢!即使内核已经告罄了,空闲链表还是空闲链表,它不会因为内核的紧张而释放自己,说白了就是没有大局观,哈!不能全局控制,且先这么看吧!接着往下走。
那内核面对这个问题该怎么办呢?于是内核大牛们提供了slab分配器来管理这些空闲链表。
(来自LKD:slab分配器的概念首先在Sun公司的SunOS5.4系统中得以实现)
怎么管理的呢?:slab层首先把不同的对象划分成高速缓存组。每个高速缓存存放不同类型的对象,LKD这句话真是让Og有些反映不过来(Og挺笨的哈!)。
其实呢!就是每个对象都有一个高速缓存去管理,因为有很多对象就有很多的高速缓存,一多就成了高速缓存组!
那这些高速缓存是怎样管理各自对应的对象(数据结构)的呢!哈哈~终于谈到链表了,上面说的类似空闲链表的东西就在高速缓存里,只不过一个高速缓存放着多条这样的链表,有全空的,有半空的,有满的,我们管这些链表叫什么呢!slab!!!现在明白为什么管这叫slab了吧!
好!我们屡一屡,首先内核要频繁的使用一些种类的对象或者数据结构,于是就把这些类型的对象或者数据结构形成了缓存,一个类型放一个高速缓存里,一个高速缓存设有好几条slab,slab里面都是这种类型的数据结构。
这样slab层概念上就有了,那么内核是怎么实现的呢?内核为高速缓存和slab虚拟了两个数据结构,高速缓存对应kmem_cache,slab当然就对应slab结构,用这两个结构来描述高速缓存和slab。
我们来看看这这两个数据结构:
先来看看高速缓存的结构。
struct kmem_cache {
struct kmem_list3 *nodelists[MAX_NUMNODES];
}
这是简化的高速缓存结构kmem_cache,其中有一个字段是kmem_list3这个结构中放着三个链表,看代码:
struct kmem_list3 {
struct list_head slabs_partial; /* partial list first, better asm code */
struct list_head slabs_full;
struct list_head slabs_free;
};
看见了吗?这是简化了的kmem_list3结构,这三个链表分别表示空的slab,半空的slab,满的slab。内核如果想要这些数据结构首先从半满的slab中拿,没有半满的就拿全空的,下面看下描述slab的结构:
struct slab {
struct list_head list;
unsigned long colouroff;
void *s_mem; /* including colour offset */
unsigned int inuse; /* num of objs active in slab */
kmem_bufctl_t free;
unsigned short nodeid;
};
这个是真正的描述slab的结构,没有简化的哦~
list表示这个slab链表的头,是满的,还是部分满或者是全空的~~
colouroff slab着色的偏移量
s_mem在slab中的第一个元素,数据结构,或者对象。
inuse 这个slab中以经使用的多少对象。
free 第一个空闲的对象。
这个描述slab的结构放在哪里呢?如果slab本身空间够用就放在slab的开始位置,如果不够用就需要另外分配。
下面说下slab分配器是怎么解决最开始说的那个空闲链表不能全局控制的问题。这就涉及到一些函数了,大家做好准备哈~:
首先要考虑的是:如果发现了某些内核频繁使用的数据结构,就要为这些结构建立缓存,这就需要slab分配器来创建这样的空间来建立缓存,怎么建立?看下面的函数:
static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid)
{
struct page *page;
int nr_pages;
int i;
flags |= cachep->gfpflags;
if(likely(nodeid == -1)){
addr = (void*)__get_free_pages(flags, cache->gfporder);
if (!addr)
return NULL;
page = virt_to_page(addr);
}else{
page = alloc_pages_node(nodeid, flags, cachep->gfporder);
if(!page)
return NULL;
addr = page_address(page);
}
i = (1 << cachep->gfporder);
if(cachep->flags & SLAB_RECLAIM_ACCOUNT)
atomic_add(i, &slab_reclaim_pages);
add_page_state(nr_slab, i_;
while(i--){
SetPageSlab(page);
page++;
}
return addr;
}
大家可以看到__get_free_pages这个函数,这就是为高速缓存分配内存的,
如果想释放内存,就要用到kmem_freepages函数了,这个函数会调用free_page函数来完成内存的释放,当然只有在高速缓存中的slab都为空的时候才能进行内存的释放!
static void kmem_freepages(struct kmem_cache *cachep, void *addr)
{
unsigned long i = (1 << cachep->gfporder);
struct page *page = virt_to_page(addr);
const unsigned long nr_freed = i;
kmemcheck_free_shadow(page, cachep->gfporder);
if (cachep->flags & SLAB_RECLAIM_ACCOUNT)
sub_zone_page_state(page_zone(page),
NR_SLAB_RECLAIMABLE, nr_freed);
else
sub_zone_page_state(page_zone(page),
NR_SLAB_UNRECLAIMABLE, nr_freed);
while (i--) {
BUG_ON(!PageSlab(page));
__ClearPageSlab(page);
page++;
}
if (current->reclaim_state)
current->reclaim_state->reclaimed_slab += nr_freed;
free_pages((unsigned long)addr, cachep->gfporder);
}
看见了吗?那个最后的free_pages函数?
这就是基本的slab层的实现。