Slab Allocator Interface)
创建新的缓存
/*
* kmem_cache_create_usercopy - 创建一个缓存.
* @name: 一个用于 /proc/slabinfo识别此缓存.
* @size: 要在此缓存中创建的对象的大小。
* @align:对象所需的对齐方式。
* @flags: SLAB标志
* @useroffset: 用户复制区域偏移
* @usersize:用户复制区域大小
* @ctor: 对象的构造函数。
*
* 如果成功,则将ptr返回到缓存,如果失败,则返回NULL。
* 不能在中断内调用,但可以被中断。
* 当高速缓存分配新页面时,将运行@ctor。
*
* flage
*
* %SLAB_POISON - Poison the slab with a known test pattern (a5a5a5a5)
* to catch references to uninitialised memory. 用已知的测试模式(a5a5a5a5)毒化平板,以捕获对未初始化内存的引用。
* 该标志使平板层用已知值(a5a5a5a5)填充平板。这称为中毒,对于捕获对未初始化内存的访问很有用。
*
*
* %SLAB_RED_ZONE - Insert `Red' zones around the allocated memory to check
* for buffer overruns. 在分配的内存周围插入“红色”区域,以检查缓冲区溢出。
* 此标志使slab层在分配的内存周围插入“红色区域”,以帮助检测缓冲区溢出。
*
* %SLAB_HWCACHE_ALIGN - Align the objects in this cache to a hardware
* cacheline. This can be beneficial if you're counting cycles as closely
* as davem.将此缓存中的对象与硬件缓存行对齐。 如果您要像davem一样对周期进行计数,那么这将是有益的。
* 该标志指示平板层将平板中的每个对象与高速缓存线对齐。
* 这样可以防止“错误共享”(尽管存在于内存中的不同地址,两个或更多对象映射到同一高速缓存行)。
* 这样可以提高性能,但会以增加内存占用为代价,因为更严格的对齐方式会浪费更多的空闲空间。
* 内存消耗增加的幅度有多大取决于对象的大小以及它们相对于系统的缓存行自然对齐的方式。
* 对于性能至关重要的代码中经常使用的缓存,设置此选项是一个好主意。否则,请三思。
*
*/
struct kmem_cache *
kmem_cache_create_usercopy(const char *name,
unsigned int size, unsigned int align,
slab_flags_t flags,
unsigned int useroffset, unsigned int usersize,
void (*ctor)(void *))
{
struct kmem_cache *s = NULL;
const char *cache_name;
int err;
get_online_cpus();
get_online_mems();
memcg_get_cache_ids();
mutex_lock(&slab_mutex);
err = kmem_cache_sanity_check(name, size);
if (err) {
goto out_unlock;
}
/* 使用分配器特定标志拒绝请求 Refuse requests with allocator specific flags */
if (flags & ~SLAB_FLAGS_PERMITTED) {
err = -EINVAL;
goto out_unlock;
}
/*
* Some allocators will constraint the set of valid flags to a subset
* of all flags. We expect them to define CACHE_CREATE_MASK in this
* case, and we'll just provide them with a sanitized version of the
* passed flags.
* 一些分配器会将有效标志集限制为所有标志的子集。
* 在这种情况下,我们希望它们定义CACHE_CREATE_MASK,我们将仅为它们提供传递标志的已清理版本。
*/
flags &= CACHE_CREATE_MASK;
/* 由于useroffset值的用户大小错误,关闭失败。 Fail closed on bad usersize of useroffset values. */
if (WARN_ON(!usersize && useroffset) ||
WARN_ON(size < usersize || size - usersize < useroffset))
usersize = useroffset = 0;
if (!usersize)
s = __kmem_cache_alias(name, size, align, flags, ctor);
if (s)
goto out_unlock;
cache_name = kstrdup_const(name, GFP_KERNEL);
if (!cache_name) {
err = -ENOMEM;
goto out_unlock;
}
s = create_cache(cache_name, size,
calculate_alignment(flags, align, size),
flags, useroffset, usersize, ctor, NULL, NULL);
if (IS_ERR(s)) {
err = PTR_ERR(s);
kfree_const(cache_name);
}
out_unlock:
mutex_unlock(&slab_mutex);
memcg_put_cache_ids();
put_online_mems();
put_online_cpus();
if (err) {
if (flags & SLAB_PANIC)
panic("kmem_cache_create: Failed to create slab '%s'. Error %d\n",
name, err);
else {
pr_warn("kmem_cache_create(%s) failed with error %d\n",
name, err);
dump_stack();
}
return NULL;
}
return s;
}
EXPORT_SYMBOL(kmem_cache_create_usercopy);
struct kmem_cache *
kmem_cache_create(const char *name, unsigned int size, unsigned int align,
slab_flags_t flags, void (*ctor)(void *))
{
return kmem_cache_create_usercopy(name, size, align, flags, 0, 0,
ctor);
}
EXPORT_SYMBOL(kmem_cache_create);
kmem_cache_create()只是分配size大小的缓存,并不会调用对象的构造函数,只有当再调用kmem_cache_alloc()时才会构造对象,另外调用kmem_cache_create()并没有分配slab,是在创建对象的时候发现没有空闲对象,调用cache_grow()分配一个slab,然后再分配对象。
例子
实例解析:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/gfp.h>
MODULE_LICENSE("GPL");
struct slab_test{
int val;
};
static int num;
struct kmem_cache *test_cachep = NULL;
struct slab_test *object1 = NULL, *object2 = NULL;
void slab_ctor(void *cachep){
printk("slab_ctor is called! object %d has been inited!\n", num);
num++;
}
static int __init kmem_cache_create_init(void){
printk("slab test module init\n");
num = 0;
test_cachep = kmem_cache_create("slab_test_cachep00000000", sizeof(struct slab_test), 0, SLAB_HWCACHE_ALIGN, slab_ctor);
if(!test_cachep)
return -ENOMEM;
object1 = kmem_cache_alloc(test_cachep, GFP_KERNEL);
if(!object1)
return -ENOMEM;
else
printk("object one has been created!\n");
object2 = kmem_cache_alloc(test_cachep, GFP_KERNEL);
if(!object2)
return -ENOMEM;
else
printk("object two has been created!\n");
return 0;
}
static void __exit kmem_cache_create_exit(void){
printk("slab test module exit\n");
kmem_cache_free(test_cachep, object1);
kmem_cache_free(test_cachep, object2);
if(test_cachep)
kmem_cache_destroy(test_cachep);
}
module_init(kmem_cache_create_init);
module_exit(kmem_cache_create_exit);
obj-m := slab.o
ccflags-y+=-DDEBUG
#ccflags-y+=-DUSE_PLATFORM_DEVICE
KDIR := /home/sys/linux-4.19.133/
all:
make -C $(KDIR) M=$(PWD) modules -Werror=implicit-function-declaration
clean:
rm -f *.o *.ko *.order *.symvers *.mod.c
可以看出,在没调用kmem_cache_alloc()前,object只是初始化了,但是还没有被构造。
[14078.026038] slab test module init
[14078.026071] slab_ctor is called! object 0 has been inited!
[14078.026071] slab_ctor is called! object 1 has been inited!
[14078.026072] slab_ctor is called! object 2 has been inited!
...............................
[14078.026139] slab_ctor is called! object 255 has been inited!
[14078.026140] object one has been created!
[14078.026140] object two has been created!
查看一下slabinfo,可以看到我们申请的缓存信息
# name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>
slab_test_cachep00000000 256 256 16 256 1 : tunables 0 0 0 : slabdata 1 1 0