在页分配器之上就是我们熟悉的slub分配器了。
slub分配器的出现很容易理解
毕竟不是谁都需要以页为单位申请内存
将申请对象按照大小分类,减少内部碎片
增加了调试功能
从API开始
先来看看平时要怎么使用slub。没见过猪跑,猪肉估计还是吃过的。
最常用的就是这一组了
kmalloc
kfree
内核对普通的内存分配预设了指定大小的slub分配器。所以当我们使用这一组API时,内核会根据我们需要分配的大小找到合适的块来分配。
除此之外还有一组API可以帮助我们定义自己制定的slub分配器
kmem_cache_create
kmem_cache_alloc
kmem_cache_free
当我们使用这一组API的时候,内核就会专门给我们增加一个slub分配器,后续的内存会专门由这个分配器分配。
一个栗子
光说不练假把式,那我们现在就来看看一个具体的例子~
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
MODULE_LICENSE("Dual BSD/GPL");
#define ALLOC_NUM 32
static struct kmem_cache *test_cache;
void* tmp[ALLOC_NUM];
static void init_once(void *foo)
{
static int num;
printk(KERN_ERR "%s: [%02d]%p is created\n", __func__, num++, foo);
}
static int kmem_cache_test_init(void)
{
int i;
test_cache = kmem_cache_create("test_cache", 1234, 4,
0, init_once);
if (test_cache == NULL)
return -ENOMEM;
printk(KERN_ERR "%s: Fetch a cache from kmem_cache\n", __func__);
for (i = 0; i < ALLOC_NUM; i++)
tmp[i] = kmem_cache_zalloc(test_cache, GFP_KERNEL);
return 0;
}
static void kmem_cache_test_exit(void)
{
int i;
for (i = 0; i < ALLOC_NUM; i++)
kmem_cache_free(test_cache, tmp[i]);
kmem_cache_destroy(test_cache);
}
module_init(kmem_cache_test_init);
module_exit(kmem_cache_test_exit);
这个例子非常简单
新建一个名为test_cache的slub分配器
从这个slub分配器中分配了32个对象
每个对象都有一个构造函数
这就是一个非常简单的内核模块,大家可以在自己的linux系统上编译运行。亲测,可用。
话说很久以前我还修复过一个slub中构造函数的一个bug。当时的版本中第一个对象会被构造两次。这个问题由这个补丁修复。
简单观测
系统中一共有两个地方可以观测到slub的情况。
/proc/slabinfo
/sys/kernel/slab/xxx/
比如我们cat /proc/slabinfo能够得到
slabinfo - version: 2.1
# name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>
test_cache 52 52 1248 26 8 : tunables 0 0 0 : slabdata 2 2 0
这个输出的格式确实有点丑,不知道哪天哪位高手可以整好看一点。
从这个输出的结果可以看到,现在一共有52个test_cache的对象,实际上我们只分配了32个不是?这也是slub的工作之一,会预先多分配一些对象,而不是每次重新分配。
当然这个文件还可以查看究竟那个模块,哪个部分用的内存最多。你说是不是?