一、前言 #
kmem_cache是slab管理器,每一个slab都是由kmem_cache进行管理的。struct kmem_cache
有三种定义,SLAB、SLUB、SLOB。内核在高版本已经使用SLUB,当前介绍一下SLUB定义的kmem_cache相关原理,另外两个是类似的实现。
SLAB是基础,SLOB用于嵌入式的小内存管理,SLUB是SLAB的改进版
二、实例 #
1. 使用kmem_cache定义一个hash表,管理用户态设置内存 #
1// 定义kmem_cache的slab
2struct kmem_cache *toa_ipv6_node_slab = NULL;
3// toa的ipv6表
4SDP_DEFINE_HASHTABLE_BL(toa_ipv6_table, TOA_IPV6_HASH_BITS);
5
6#ifdef DEBUG
7#define TOA_IPV6_SLAB_FLAG (SLAB_RED_ZONE | SLAB_POISON | SLAB_STORE_USER | SLAB_CONSISTENCY_CHECKS)
8#else
9#define TOA_IPV6_SLAB_FLAG 0
10#endif
11
12// 初始化kmemcache
13static int init_toa_ipv6_slab(void) {
14 static const char *tag = __FUNCTION__;
15 // 定义的是使用struct toa_ipv6_node的对应大小,后面malloc就是这个大小
16 toa_ipv6_node_slab = KMEM_CACHE(toa_ipv6_node, TOA_IPV6_SLAB_FLAG);
17 if (toa_ipv6_node_slab == NULL) {
18 LOG_ERR("%s, create slab failed, maybe out of memory", tag);
19 return -1;
20 }
21 return 0;
22}
23
24// 仅允许驱动卸载调用,里面会清理hash表,不加锁
25static void uninit_toa_ipv6_slab(void) {
26 static const char *tag = __FUNCTION__;
27 int idx;
28 int count = 0;
29 struct hlist_bl_node *tmp = NULL;
30 struct hlist_bl_node *node = NULL;
31 toa_ipv6_node_t *entry = NULL;
32
33 BUG_ON(!toa_ipv6_node_slab);
34
35 // kmem_cache在销毁slab前需要将所有的slab回收掉才行
36 hash_for_each_safe_bl(toa_ipv6_table, idx, tmp, node, entry, list) {
37 hlist_bl_del(node);
38 kmem_cache_free(toa_ipv6_node_slab, entry);
39 ++count;
40 }
41 LOG_INFO("%s, there are %d ipv6 nodes not freed", tag, count);
42
43 kmem_cache_destroy(toa_ipv6_node_slab);
44 toa_ipv6_node_slab = NULL;
45}
46
47// 使用
48void test(void) {
49 struct toa_ipv6_node *node = NULL;
50 node = kmem_cache_zalloc(toa_ipv6_node_slab, GFP_KERNEL);
51 // do something
52}
三、代码原理 #
1. 先解释一下struct kmem_cache
#
1// include/linux/slub_def.h
2/*
3 * Slab cache management.
4 */
5struct kmem_cache {
6 // 每个cpu的私有数据,用于特定场景的快速申请和释放
7 struct kmem_cache_cpu __percpu *cpu_slab;
8
9 /* Used for retrieving partial slabs, etc. */
10 // 记录分配时要设置的flags
11 // SLAB_CONSISTENCY_CHECKS 用于检查内存分配器(slab 分配器)的内部一致性,启用这个选项可以帮助开发人员检测和诊断内存管理中的错误和不一致问题
12 // - 内存块完整性
13 // - 内存池状态
14 // - 边界检查
15 // - 双重释放
16 // SLAB_RED_ZONE 用于在缓存中划分红色区域,以检测内存越界等问题
17 // SLAB_POISON 用于填充对象以防止使用未初始化的内存或检出内存越界访问
18 // SLAB_HWCACHE_ALIGN 用于将对象对齐到缓存行,以提高缓存命中率和性能
19 // SLAB_CACHE_DMA 对应于使用GFP_DMA
20 // SLAB_CACHE_DMA32 对应于使用GFP_DMA32
21 // SLAB_STORE_USER 用于存储最后的使用者信息,帮助在调试过程中追踪内存使用
22 // SLAB_PANIC 函数调用失败时触发panic,从而中止系统运行,这是为了确保系统不会在内存分配错误后继续运行,导致更严重的问题
23 slab_flags_t flags;
24
25 // kmem_cache_shrink进行内存缩减时,要保留的最小的值。由函数set_min_partial(s, ilog2(s->size)/2)设置
26 unsigned long min_partial;
27
28 // object的实际大小,包含对齐
29 unsigned int size; /* The size of an object including metadata */
30 // object的payload大小,数据结构实际使用的大小
31 unsigned int object_size;/* The size of an object without metadata */
32
33
34 struct reciprocal_value reciprocal_size;
35 unsigned int offset; /* Free pointer offset */
36#ifdef CONFIG_SLUB_CPU_PARTIAL
37 /* Number of per cpu partial objects to keep around */
38 unsigned int cpu_partial;
39 /* Number of per cpu partial slabs to keep around */
40 unsigned int cpu_partial_slabs;
41#endif
42 struct kmem_cache_order_objects oo;
43
44 /* Allocation and freeing of slabs */
45 struct kmem_cache_order_objects min;
46 gfp_t allocflags; /* gfp flags to use on each alloc */
47 int refcount; /* Refcount for slab cache destroy */
48 void (*ctor)(void *);
49 unsigned int inuse; /* Offset to metadata */
50 unsigned int align; /* Alignment */
51 unsigned int red_left_pad; /* Left redzone padding size */
52 const char *name; /* Name (only for display!) */
53 struct list_head list; /* List of slab caches */
54#ifdef CONFIG_SYSFS
55 struct kobject kobj; /* For sysfs */
56#endif
57#ifdef CONFIG_SLAB_FREELIST_HARDENED
58 unsigned long random;
59#endif
60
61#ifdef CONFIG_NUMA
62 /*
63 * Defragmentation by allocating from a remote node.
64 */
65 unsigned int remote_node_defrag_ratio;
66#endif
67
68#ifdef CONFIG_SLAB_FREELIST_RANDOM
69 unsigned int *random_seq;
70#endif
71
72#ifdef CONFIG_KASAN
73 struct kasan_cache kasan_info;
74#endif
75
76 unsigned int useroffset; /* Usercopy region offset */
77 unsigned int usersize; /* Usercopy region size */
78
79 struct kmem_cache_node *node[MAX_NUMNODES];
80};