分析linux内核中的slub内存管理算法
1. 分析的linux内核源码版本为4.18.0
2. 与slub相关的内核配置项为CONFIG_SLUB
3. 一切都从一个结构体数组kmalloc_caches开始,它的原型如下:
struct kmem_cache *kmalloc_caches[KMALLOC_SHIFT_HIGH + 1] __ro_after_init;
3.1 这个数组定义在mm/slab_common.c中
3.2 KMALLOC_SHIFT_HIGH是如何定义的呢?
#define KMALLOC_SHIFT_HIGH (PAGE_SHIFT + 1)
#define PAGE_SHIFT 12 (各个架构下的定义都有些差异,如果是arm64,那么是通过CONFIG_ARM64_PAGE_SHIFT来指定的,这个配置项在arch/arm64/Kconfig文件中定义,默认为12,也就是默认页面大小为4KiB,笔者以arm64为例)
那么KMALLOC_SHIFT_HIGH=PAGE_SHIFT + 1 = 12 + 1 = 13,KMALLOC_SHIFT_HIGH+1=13+ 1= 14说明kmalloc_caches数组中有14个元素,每个元素是kmem_cache这个结构体
3.3 分析一下sturct kmem_cache这个结构体
/* * Slab cache management. */ struct kmem_cache { struct kmem_cache_cpu __percpu *cpu_slab; /* Used for retriving partial slabs etc */ slab_flags_t flags; unsigned long min_partial; unsigned int size; /* The size of an object including meta data */ unsigned int object_size;/* The size of an object without meta data */ unsigned int offset; /* Free pointer offset. */ #ifdef CONFIG_SLUB_CPU_PARTIAL /* Number of per cpu partial objects to keep around */ unsigned int cpu_partial; #endif struct kmem_cache_order_objects oo; /* Allocation and freeing of slabs */ struct kmem_cache_order_objects max; struct kmem_cache_order_objects min; gfp_t allocflags; /* gfp flags to use on each alloc */ int refcount; /* Refcount for slab cache destroy */ void (*ctor)(void *); unsigned int inuse; /* Offset to metadata */ unsigned int align; /* Alignment */ unsigned int red_left_pad; /* Left redzone padding size */ const char *name; /* Name (only for display!) */ struct list_head list; /* List of slab caches */ #ifdef CONFIG_SYSFS struct kobject kobj; /* For sysfs */ struct work_struct kobj_remove_work; #endif #ifdef CONFIG_MEMCG struct memcg_cache_params memcg_params; /* for propagation, maximum size of a stored attr */ unsigned int max_attr_size; #ifdef CONFIG_SYSFS struct kset *memcg_kset; #endif #endif #ifdef CONFIG_SLAB_FREELIST_HARDENED unsigned long random; #endif #ifdef CONFIG_NUMA /* * Defragmentation by allocating from a remote node. */ unsigned int remote_node_defrag_ratio; #endif #ifdef CONFIG_SLAB_FREELIST_RANDOM unsigned int *random_seq; #endif #ifdef CONFIG_KASAN struct kasan_cache kasan_info; #endif unsigned int useroffset; /* Usercopy region offset */ unsigned int usersize; /* Usercopy region size */ struct kmem_cache_node *node[MAX_NUMNODES]; };
3.4 struct kmem_cache中有哪些域是需要关注到的呢?
3.4.1 node
struct kmem_cache_node *node[MAX_NUMNODES];
这里面MAX_NUMNODES定义如下:
#define MAX_NUMNODES (1 << NODES_SHIFT)
那么NODES_SHIFT又是如何定义的呢?
#ifdef CONFIG_NODES_SHIFT #define NODES_SHIFT CONFIG_NODES_SHIFT #else #define NODES_SHIFT 0 #endif
如果定义了CONFIG_NODES_SHIFT,那么NODES_SHIFT就等于CONFIG_NODES_SHIFT的值;
如果未定义CONFIG_NODES_SHIFT,那么NODES_SHIFT就等于0;
假设未定义CONFIG_NODES_SHIFT,那么MAX_NUMNODES就等于1,也就是只有一个kmem_cache_node节点.
3.4.2 cpu_slab
struct kmem_cache_cpu __percpu *cpu_slab;
表示每个cpu都具有一个这个的结构来描述当前slab的情况
__percpu是什么?
# define __percpu __attribute__((noderef, address_space(3)))
__percpu表示一种特性,是用来修饰变量的.noderef指定这个变量必须是有效的,address_space(3)则指定变量所在的地址空间为3,也就是cpu空间.作用就是保证每个cpu都有这个变量的副本
3.4.3 size
unsigned int size; /* The size of an object including meta data */
表示包含元数据的一个object的大小
3.4.4 object_size
unsigned int object_size;/* The size of an object without meta data */
表示不包含元数据的一个object的大小
3.4.5 offset
unsigned int offset; /* Free pointer offset. */
表示空闲指针的偏移量
3.5 __ro_after_init是什么东西?
#define __ro_after_init __attribute__((__section__(".data..ro_after_init")))
这是一个宏,定义在include/linux/cache.h中,被用来标记初始化之后只读的内容
这里面涉及到一个段.data..ro_after_init,可以在include/asm-generic/vmlinux.lds.h中找到
1 #ifndef RO_AFTER_INIT_DATA 2 #define RO_AFTER_INIT_DATA \ 3 __start_ro_after_init = .; \ 4 *(.data..ro_after_init) \ 5 __end_ro_after_init = .; 6 #endif
4. 如何填充kmalloc_caches数组的呢?
start_kernel()-> (init/main.c)
mm_init()-> (init/main.c)
kmem_cache_init()-> (mm/slub.c)
create_kmalloc_caches()-> (mm/slab_common.c)
new_kamalloc_cache()-> (mm/slab_common.c)
create_kmalloc_cache()-> (mm/slab_common.c)
从源码中可以得知kmalloc_caches数组由create_kmalloc_cache()填充每一个数组中的元素
5. slub中支持的object的大小范围是多少?
每个kmalloc_caches中的元素会使用结构体kmem_cache中的域size和objsize来指定slab中每个object的大小,object的大小从全局常量结构体数组kmalloc_info中获取
const struct kmalloc_info_struct kmalloc_info[] __initconst = { {NULL, 0}, {"kmalloc-96", 96}, {"kmalloc-192", 192}, {"kmalloc-8", 8}, {"kmalloc-16", 16}, {"kmalloc-32", 32}, {"kmalloc-64", 64}, {"kmalloc-128", 128}, {"kmalloc-256", 256}, {"kmalloc-512", 512}, {"kmalloc-1024", 1024}, {"kmalloc-2048", 2048}, {"kmalloc-4096", 4096}, {"kmalloc-8192", 8192}, {"kmalloc-16384", 16384}, {"kmalloc-32768", 32768}, {"kmalloc-65536", 65536}, {"kmalloc-131072", 131072}, {"kmalloc-262144", 262144}, {"kmalloc-524288", 524288}, {"kmalloc-1048576", 1048576}, {"kmalloc-2097152", 2097152}, {"kmalloc-4194304", 4194304}, {"kmalloc-8388608", 8388608}, {"kmalloc-16777216", 16777216}, {"kmalloc-33554432", 33554432}, {"kmalloc-67108864", 67108864} };
从数组中的最后一个元素可以获知支持的最大object的大小为2^26=64MiB,但是从以下代码分析:
void __init create_kmalloc_caches(slab_flags_t flags) { int i; for (i = KMALLOC_SHIFT_LOW; i <= KMALLOC_SHIFT_HIGH; i++) { if (!kmalloc_caches[i]) new_kmalloc_cache(i, flags);
可得:
kmalloc_caches数组中仅下标为KMALLOC_SHIFT_LOW到KMALLOC_SHIFT_HIGH的元素才被初始化,
也就是从3->13支持的最小object大小为2^3=8字节,最大object大小为2^13=8KiB,说明kmalloc_caches数组的前三个元素并没有被初始化,仅初始化数组的后面11个元素.
从以上分析可得slub支持的最大object的大小为页面大小的两倍(PAGE_SIZE*2),页面大小为4KiB,那么最大object的大小为4KiB * 2 = 8KiB