Linux内核模块开发之创建slab内存缓存(kmem_cache_*)
一、引言
1.1、Linux内核中的内存管理机制
Linux内核中的内存管理机制是指对系统内存的分配、释放、共享、保护和映射等方面进行管理和控制的一系列机制。其作用是保证系统内存的高效利用和有效保护,并为进程提供可靠的内存访问和管理能力。
Linux内核中的内存管理机制包括:
-
物理内存管理:管理系统物理内存的分配、释放和保护,包括页面分配、页面回收、页面置换和页面共享等操作。
-
虚拟内存管理:管理进程虚拟地址空间的分配、映射和保护,包括页表管理、页面映射和内存保护等操作。
-
缓存管理:管理系统内存缓存的分配、释放和保护,包括slab缓存、page缓存和inode缓存等操作。
-
内存统计和监控:对系统内存的使用情况进行统计和监控,包括内存使用量、内存泄漏、内存碎片和内存性能等指标的评估和监测。
-
内存优化和调优:针对系统内存的性能和效率进行优化和调优,包括内存大小、缓存大小、页面置换策略、内存映射方式等参数的设置和调整。
Linux内核中的内存管理机制是系统运行和应用开发的基础,其优化和调优对于提高系统性能和可靠性具有重要意义。
1.2、内存管理机制的作用
内存管理机制的作用:内存管理机制是操作系统用于分配、保护和共享内存资源的基础机制。
- 管理物理内存资源,包括内存分配、释放和保护等操作。
- 管理虚拟内存资源,包括地址映射、页面管理和内存保护等操作。
- 管理内存缓存,包括页面缓存、inode缓存和slab缓存等操作。
- 监控和统计内存使用情况,包括内存使用量、内存泄漏和内存碎片等指标的评估和监测。
- 优化和调整内存管理策略,以提高系统性能和可靠性。
内存管理机制重要性:
- 内存管理机制直接关系到系统整体的性能和可靠性,能否高效、稳定地管理内存资源,直接影响到系统的运行效率和稳定性。
- 内存管理机制是系统运行和应用开发的基础,只有掌握了内存管理机制的原理和作用,才能更好地进行系统优化和开发工作。
- 内存管理机制是操作系统保护机制的重要组成部分,可以有效地防止内存泄漏、内存覆盖和内存访问冲突等问题,保护系统和应用程序的安全和稳定性。
二、slab内存缓存的概念
2.1、什么是slab内存缓存?
Slab内存缓存是Linux内核中的一种内存缓存机制,它是一种高效、可靠的内存管理方式,用于快速分配和释放固定大小的内存块。该机制通过维护一组预分配的内存块来提高内存分配和释放的效率,从而减少内存分配和释放的开销,并提高系统的性能和可靠性。
Slab内存缓存将内存分为若干个固定大小的块,并将这些块存储在一个内存池中,每个块都标记着它所属的缓存。当进程需要使用内存时,Slab内存缓存会快速从内存池中分配一个可用的内存块,并返回给进程使用。当进程不再需要使用这个内存块时,Slab内存缓存会将它归还给内存池,以便下次重复利用,从而减少内存分配和释放的开销。
Slab内存缓存机制的优点包括:
- 减少内存分配和释放的开销,提高系统性能和可靠性;
- 避免了内存碎片和内存泄漏等问题,减少了系统运行中的问题;
- 支持多种内存管理策略,能够满足不同场景下的内存需求。
2.2、slab内存缓存的结构和特点
Slab内存缓存由以下三个基本单元组成:
- Slab cache:一个Slab cache包含若干个Slab,每个Slab都由一组连续的内存块构成。
- Slab:一个Slab是一组连续的内存块,它们的大小相同且等于一个预定义的固定值。
- 内存块:一个内存块是一个固定大小的内存区域,它可以被分配给进程使用。
Slab内存缓存的特点:
- 高效:Slab内存缓存通过预分配一组固定大小的内存块来避免内存分配和释放的开销,从而提高内存管理的效率。
- 可靠:Slab内存缓存支持多种内存管理策略,可以有效地避免内存碎片和内存泄漏等问题,保证系统的稳定性。
- Slab内存缓存可以根据不同的应用场景和需要,灵活地调整内存管理策略和配置参数。
- Slab内存缓存提供了一组简单、易于使用的API,可以方便地管理和操作内存缓存,减少了管理复杂度和难度。
- Slab内存缓存可以同时服务多个进程,支持高并发访问,从而提高系统的吞吐量和响应能力。
三、创建专用的内存缓存编程接口
流程:
- 创建内存缓存 kmem_cache_create。
- 指定内存缓存分配 kmem_cache_alloc。
- 释放对象 kmem_cache_free。
- 销毁内存缓存 keme_cache_destroy。
函数原型:
void *kmem_cache_alloc(struct kmem_cache *cachep, int flags);
void kmem_cache_free(struct kmem_cache *cachep, void *objp);
struct kmem_cache *
kmem_cache_create(const char *name, size_t size, size_t offset,
unsigned long flags, void (*ctor)(void *));
void kmem_cache_destroy(struct kmem_cache *);
四、实现步骤
- 定义初始化模块和退出模块函数。
- 定义一个kmem_cache全局变量。
- 在初始化模块函数调用kmem_cache_create函数,指定模块名称。
- 如果创建成功,可以调用kmem_cache_size获取缓存大小。
- 退出模块调用keme_cache_destroy删除函数指针。
- 模块初始化操作和退出函数调用module_init()和module_exit()。
- 其他的声明信息,比如许可协议、作者、模块功能描述等等。
五、内存缓存的数据结构
(include/linux/slab_def.h)
struct kmem_cache {
struct array_cache __percpu *cpu_cache;
/* 1) Cache tunables. Protected by slab_mutex */
unsigned int batchcount;
unsigned int limit;
unsigned int shared;
unsigned int size;
struct reciprocal_value reciprocal_buffer_size;
/* 2) touched by every alloc & free from the backend */
slab_flags_t flags; /* constant flags */
unsigned int num; /* # of objs per slab */
/* 3) cache_grow/shrink */
/* order of pgs per slab (2^n) */
unsigned int gfporder;
/* force GFP flags, e.g. GFP_DMA */
gfp_t allocflags;
size_t colour; /* cache colouring range */
unsigned int colour_off; /* colour offset */
struct kmem_cache *freelist_cache;
unsigned int freelist_size;
/* constructor func */
void (*ctor)(void *obj);
/* 4) cache creation/removal */
const char *name;
struct list_head list;
int refcount;
int object_size;
int align;
/* 5) statistics */
#ifdef CONFIG_DEBUG_SLAB
unsigned long num_active;
unsigned long num_allocations;
unsigned long high_mark;
unsigned long grown;
unsigned long reaped;
unsigned long errors;
unsigned long max_freeable;
unsigned long node_allocs;
unsigned long node_frees;
unsigned long node_overflow;
atomic_t allochit;
atomic_t allocmiss;
atomic_t freehit;
atomic_t freemiss;
/*
* If debugging is enabled, then the allocator can add additional
* fields and/or padding to every object. 'size' contains the total
* object size including these internal fields, while 'obj_offset'
* and 'object_size' contain the offset to the user object and its
* size.
*/
int obj_offset;
#endif /* CONFIG_DEBUG_SLAB */
#ifdef CONFIG_MEMCG
struct memcg_cache_params memcg_params;
#endif
#ifdef CONFIG_KASAN
struct kasan_cache kasan_info;
#endif
#ifdef CONFIG_SLAB_FREELIST_RANDOM
unsigned int *random_seq;
#endif
unsigned int useroffset; /* Usercopy region offset */
unsigned int usersize; /* Usercopy region size */
struct kmem_cache_node *node[MAX_NUMNODES];
};
内存缓存的数据结构如下图所示:
六、完整代码示例
6.1、源代码
kmemd.c
/* 头文件和全局变量地声明*/
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
static int __init kmem_cache_create_InitFunc(void);
static void __exit kmem_cache_create_ExitFunc(void);
struct kmem_cache *pmycache = NULL;
// 模块初始化函数
int __init kmem_cache_create_InitFunc(void)
{
pmycache = kmem_cache_create("FlyCache",64,0, SLAB_HWCACHE_ALIGN, NULL);
if(NULL==pmycache)
{
printk("执行:kmem_cache_create(...)函数失败,请重新检查?\n");
}
else
{
printk("创建slab缓存成功!\n");
printk("FlyCache Cache大小为: %d\n", kmem_cache_size(pmycache));
}
return 0;
}
// 模块退出函数
void __exit kmem_cache_create_ExitFunc(void)
{
if(pmycache)
{
kmem_cache_destroy(pmycache);
printk("执行:kmem_cache_destroy()函数释放slab缓存成功!\n");
}
printk("内核模块退出成功!\n");
}
/* 模块初始化操作和退出函数调用 */
module_init(kmem_cache_create_InitFunc);
module_exit(kmem_cache_create_ExitFunc);
MODULE_LICENSE("GPL"); /* 描述模块代码接受的软件许可协议 */
MODULE_AUTHOR("Lion Long"); /* 描述模块的作者信息:包括作者姓名及邮箱等等 */
MODULE_DESCRIPTION(" kernel module : kmem_cache_create/kmem_cache_destroy"); /* 简要描述此模块用途及功能介绍*/
Makefile
obj-m:=kmemd.o
CURRENT_PAHT:=$(shell pwd)
LINUX_KERNEL:=$(shell uname -r)
LINUX_KERNEL_PATH:=/usr/src/linux-headers-$(LINUX_KERNEL)
all:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PAHT) modules
clean:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PAHT) cleals
6.2、编译和执行
(1)make。
$ make
make -C /usr/src/linux-headers-4.15.0-142-generic M=/home/fly/workspace/linux_kernel modules
make[1]: Entering directory '/usr/src/linux-headers-4.15.0-142-generic'
CC [M] /home/fly/workspace/linux_kernel/kmemd.o
Building modules, stage 2.
MODPOST 1 modules
CC /home/fly/workspace/linux_kernel/kmemd.mod.o
LD [M] /home/fly/workspace/linux_kernel/kmemd.ko
make[1]: Leaving directory '/usr/src/linux-headers-4.15.0-142-generic'
(2)插入模块。
insmod kmemd.ko
(3)查看模块。
$ sudo dmes -c
[101921.947832] 创建slab缓存成功!
[101921.947833] FlyCache Cache大小为: 64
七、总结
本文全面介绍了Linux内核中的内存管理机制和slab内存缓存的重要性。解释了内存管理机制的作用,并深入探讨了slab内存缓存的概念、结构和特点。还讨论了创建专用的内存缓存编程接口的方法,并提供了实现步骤和完整的代码示例。