Linux内核模块开发 Slab高速缓存接口与用例

在内核模块开发或者驱动开发中经常会使用到内存分配,常见的方式是调用 kmalloc 接口分配内存。

static __always_inline void *kmalloc(size_t size, gfp_t flags);

kmalloc接口使用简单,并且不会对所获取的空间清零,分配的空间仍然保持着原有的数据。但是根据<linux设备驱动程序>一书中的说法,kmalloc能够分配的内存块大小,存在一个上限。这个限制随着体系架构的不同以及内核配置选项的不同而变化。此外,在程序中常常会反复分配很多同一大小的内存块,经常会带来重复初始化和内存碎片问题,于是内核提供了slab机制。

slab的优势

与传统的内存管理模式相比, slab 缓存分配器提供了很多优点。首先,内核通常依赖于对小对象的分配,它们会在系统生命周期内进行无数次分配。slab 缓存分配器通过对类似大小的对象进行缓存而提供这种功能,从而避免了常见的碎片问题。slab 分配器还支持通用对象的初始化,从而避免了为同一目而对一个对象重复进行初始化。最后,slab 分配器还可以支持硬件缓存对齐和着色,这允许不同缓存中的对象占用相同的缓存行,从而提高缓存的利用率并获得更好的性能。

slab的接口简单,包含slab缓存创建,对象内存分配、释放与slab缓存销毁。

/* 创建 slab 缓存结构 */
static struct kmem_cache *my_cachep;

/* 
 * slab 缓存创建 
 * 该函数创建一个新的高速缓存对象啊,其中可以容纳任意数目的内存区域,这些区域的大小都相同,
 * 由size参数指定,参数name与这个高速缓存相关联,其功能是保管一些信息以便跟踪,它通常被
 * 设置为将要高速缓存的结构类型的名字,高速缓存保留指向该名称的指针而不是复制其内容,因此
 * ,程序应该将指向静态存储的指针传递给这个函数。名称中不能有空白。
 * align参数指的是某种对齐方式,通常取值为0
 * flag控制如何分配,是一个位掩码,可以有如下取值:
 *
 * SLAB_NO_RAP : 设置这个标志可以保护高速缓存在系统寻找内存的时候不会被减少,设置该标志通常不是
 * 好主意,因为对内存分配器的自由做一些人为的、不必要的限制。
 *
 * SLAB_HWCACHE_ALIGN :
 * 这个标志要求所有数据对象跟高速缓存行对齐;实际的操作则依赖与主机平台的硬件高速缓存布局,如果在
 * SMP机器上,高速缓存中包含有频繁访问的数据项的话,该选项是一个非常好的选择,但是,为了满足高速
 * 缓存行的对齐需求,必要的填白可能浪费大量内存。
 *
 * 还有其它的一些选项,如SLAB_CACHE_DMA, SLAB_DESTROY_BY_RCU,SLAB_MEM_SPREAD
 * 具体使用哪一个根据应用决定,每个选项在源文件linux/slab.h中有详细注释
 */ 
struct kmem_cache *kmem_cache_create (const char *name, size_t size, size_t align,
	unsigned long flags, void (*ctor)(void *))

/*
 * 从缓存中分配一个对象 
 * flag标志位常用的有GFP_KERNEL, GFP_ATOMIC,区别在于前者可能会引起睡眠
 */
void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags);

/* 将一个对象释放回slab */
void kmem_cache_free( struct kmem_cache *cachep, void *objp );

/* 销毁 slab 缓存, 调用前必须确保所有对象都已经返还给slab缓存 */
void kmem_cache_destroy(struct kmem_cache *cachep);

/* 其它接口 */
//与kmem_cache_alloc类似,只不过它会对分配到的缓存清零
static inline void *kmem_cache_zalloc(struct kmem_cache *k, gfp_t flags);

下面是一个简单的slab使用的demo,包括创建slab缓存, 分配对象内存,释放对象与销毁slab缓存。

/*
 *  Description : slab高速缓存用例
 *  Author : mason
 *  Date   : 201809
 */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>

#define log_info(fmt, arg...)	printk("<3>%s:%d " fmt, __FUNCTION__ , __LINE__, ##arg)


/* 创建slab 缓存结构 */
static struct kmem_cache *slab_cachep;

static int __init slab_demo_init(void)
{
    void *object;

    log_info("slab cache init \r\n");
        
    /* 创建slab缓存,对象大小32个字节 */
    slab_cachep = kmem_cache_create("slab_demo_cache", 
                            32, 0, SLAB_DESTROY_BY_RCU, NULL);
    if (!slab_cachep)
    {
        log_info("error, create slab_cache fail! \r\n");
        return -1;
    }

    /* 分配对象内存 */
    object = kmem_cache_alloc(slab_cachep, GFP_KERNEL);  
    
    if (object) 
    {
        log_info("create slab object success \r\n");
        kmem_cache_free( slab_cachep, object );
    }
    
    return 0;
}

static void __exit slab_demo_exit(void)
{
    kmem_cache_destroy( slab_cachep );

    log_info("slab cache destroy \r\n");
}


module_init(slab_demo_init);
module_exit(slab_demo_exit);
MODULE_LICENSE("GPL");

内核版本 3.4.39

运行截图:

参考资料:

1. Linux slab 分配器剖析 :

https://www.ibm.com/developerworks/cn/linux/l-linux-slab-allocator/

2. 《linux设备驱动程序第三版》

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值