1、slab的总体设计架构
1.1 slab的基础信息管理
slab cache在内核中的数据结构为struct kmem_cache,在文件linux_4.1.15\include\linux\slub_def.h中有如下定义;
/*
* Slab cache management.
*/
struct kmem_cache {
/* 每个cpu拥有一个本地缓存,用于无锁化快速分配释放对象 */
struct kmem_cache_cpu __percpu *cpu_slab;
/* Used for retriving partial slabs etc 管理标志位,用于设置slab的特性,例如是否毒化 是否需要poison 是否插入red zone在对象内存周围 释放追踪对象的分配和释放 */
unsigned long flags;
unsigned long min_partial;
/* 对象在内存中的真实占用,包括为内存对齐填充的字节数,red zone等 */
int size; /* The size of an object including meta data */
/* slab中对象实际大小,不包括填充的字节数 */
int object_size; /* The size of an object without meta data */
/* 存储下一个空闲对象指针位置距离对象首地址的偏移 */
int offset; /* Free pointer offset. */
int cpu_partial; /* Number of per cpu partial objects to keep around */
/* 低16位表示一个slab中包括的对象总数,高16位表示一个slab所占用的内存页个数 */
struct kmem_cache_order_objects oo;
/* Allocation and freeing of slabs */
struct kmem_cache_order_objects max;
/* 当按照oo尺寸为slab申请内存时,如果内促紧张,会采用min的尺寸为slab申请内存,可以容纳一个对象即可 */
struct kmem_cache_order_objects min;
/* 向伙伴系统申请内存时使用的内存分配标识 */
gfp_t allocflags; /* gfp flags to use on each alloc */
/* slab cache的引用计数,为0时就可以销毁并释放内存回伙伴系统 */
int refcount; /* Refcount for slab cache destroy */
/* 池化对象的构造函数,用于创建slab对象池中的对象 */
void (*ctor)(void *);
/* 对象object_size按照word字长对齐之后的大小 */
int inuse; /* Offset to metadata(元数据) */
/* 按照指定的align进行对齐 */
int align; /* Alignment */
int reserved; /* Reserved bytes at the end of slabs */
/* slab cache的名称,也就是在slabinfo命令中name那一列 */
const char *name; /* Name (only for display!) */
struct list_head list; /* List of slab caches */
#ifdef CONFIG_SYSFS
struct kobject kobj; /* For sysfs */
#endif
#ifdef CONFIG_MEMCG_KMEM
struct memcg_cache_params memcg_params;
int max_attr_size; /* for propagation, maximum size of a stored attr */
#ifdef CONFIG_SYSFS
struct kset *memcg_kset;
#endif
#endif
#ifdef CONFIG_NUMA
/*
* Defragmentation by allocating from a remote node.
*/
int remote_node_defrag_ratio;
#endif
struct kmem_cache_node *node[MAX_NUMNODES];
};
- flag标志在linux_4.1.15\include\linux\slab.h文件中定义,含义如下:
#define SLAB_DEBUG_FREE 0x00000100UL /* DEBUG: Perform (expensive) checks on free */
#define SLAB_RED_ZONE 0x00000400UL /* DEBUG: Red zone objs in a cache 表示需要在slab对象的内存周围插入red zone,防止内存的读写越界 */
#define SLAB_POISON 0x00000800UL /* DEBUG: Poison objects 需要在slab对象内存中填充特殊字节0x6b和0xa5,表示对象的特殊状态 */
#define SLAB_HWCACHE_ALIGN 0x00002000UL /* Align objs on cache lines 表示slab中的对象按照cpu硬件告诉缓存cache line进行对齐 */
#define SLAB_CACHE_DMA 0x00004000UL /* Use GFP_DMA memory 表示slab中的内存来自与内存DMA或DMA32 */
#define SLAB_STORE_USER 0x00010000UL /* DEBUG: Store the last owner for bug hunting 表示跟踪对象的分配释放相关信息 */
#define SLAB_PANIC 0x00040000UL /* Panic if kmem_cache_create() fails */
其中SLAB_STORE_USER表示需要追踪对象的分配和释放相关信息,这会在slab对象区域中额外增加两个struct track大小区域出来,用于插入slab对象的分配和释放信息;
- size 表示slab对象在内存中的真实占用大小,包括对象中各种填充内存区域大小;
-
object_size 表示单纯的存储slab对象所需要的实际内存大小,如上图中的object size蓝色区域所示;
-
name就是该slab cache的名字,也就是cat /proc/slabinfo中看到的name信息;
-
struct list_head list; /* List of slab caches */ 系统中所有的 slab cache,内核会将这些 slab cache 用一个双向链表统一串联起来。链表的头结点指针保存在 struct kmem_cache 结构的 list 中。
cat /proc/slabinfo显示结构主要由三部分组成:
第一部分:显示slab cache的基本统计信息;
<active_objs> <num_objs> <objsize> <objperslab> <pagesperslab>
0 0 792 10 2
active_objs: 表示slab cache中以及被分配出去的对象个数;
num_objs: 表示slab cache中容纳的对象总数;
objsize: 表示slab中对象的object size,单位为字节;
objperslab: 表示slab中可以容纳的对象个数;
pagesperslab: 表示slab所需要的物理内存页个数;
第二部分:显示slab cache中的动态可调节参数,如果采用slub实现,那么tunables部分全是0;
tunables <limit> <batchcount> <sharedfactor>
tunables 0 0 0
第三部分:显示slab cache的总体信息
slabdata <active_slabs> <num_slabs> <sharedavail>
slabdata 2 2 0
active_slabs: 表示slab cache中活跃的slab个数;
nums_slabs: 表示slab cache中管理slab总数;
- struct kmem_cache_node *node[MAX_NUMNODES]: 表示slab cache在NUMA节点中的仓库,当slab cache在本地cpu缓存kmem_cache_cpu中没有足够的内存块可供分配时,内核会到NUMA节点的仓库中拿出slab填充到kmem_cache_cpu中。
在linux_4.1.15\mm\slab.h文件中有kmem_cache_node节点的定义;
/*
* The slab lists for all objects.
*/
struct kmem_cache_node {
spinlock_t list_lock;
#ifdef CONFIG_SLAB
struct list_head slabs_partial; /* partial list first, better asm code */
struct list_head slabs_full;
struct list_head slabs_free;
unsigned long free_objects;
unsigned int free_limit;
unsigned int colour_next; /* Per-node cache coloring */
struct array_cache *shared; /* shared per node */
struct alien_cache **alien; /* on other nodes */
unsigned long next_reap; /* updated without locking */
int free_touched; /* updated without locking */
#endif
#ifdef CONFIG_SLUB
unsigned long nr_partial; // 该node节点中缓存的slab个数
struct list_head partial; // 该链表用于组织串联node节点缓存的slabs,partial链表中缓存的slab为部分空闲slab
#ifdef CONFIG_SLUB_DEBUG
atomic_long_t nr_slabs;
atomic_long_t total_objects;
struct list_head full;
#endif
#endif
};
得到slab的全貌:
上图中展示的slab cache本地cpu缓存kmem_cache_cpu中的partial列表以及NUMA节点缓存kmem_cache_node结构中的partital列表并不是无限制增长的,它们的容量受到下面两个参数的限制:
struct kmem_cache {
/* slab cache在numa node中缓存的slab个数上限,slab个数超过该值,空闲empty slab则会被回收到伙伴系统 */
int cpu_partial; /* Number of per cpu partial objects to keep around */
/* slab cache在numa node中缓存partial链表中所有slab中空闲对象的总数 */
unsigned long min_partial;
}
numa_partial:主要控制NUMA节点缓存partial列表slab个数,如果超过该值,那么列表中空闲empty slab就会被释放回收到伙伴系统中;
cpu_partial: 主要控制slab cache本地cpu缓存kmem_cache_cpu结构partial链表的空闲对象总数,如果超过该值,那么kmem_cache_cpu->partial列表中缓存的slab将会被全部移动到kmem_cache_node->partial列表中;
2、slab内存分配原理
略
3、slab的释放原理
略
4、slab cache的创建过程
在linux_4.1.15\mm\slab_common.c文件中有kmem_cache_creat函数定义,如下:
struct kmem_cache *kmem_cache_create(const char *name, size_t size, size_t align,
unsigned long flags, void (*ctor)(void *))
{
struct kmem_cache *s;
s = do_kmem_cache_create(cache_name, size, size,
calculate_alignment(flags, align, size),
flags, ctor, NULL, NULL);
}
该函数中的参数,是用用户指定的关于slab cahce的一些核心属性,在slab cache的过程中,内核会将kmem_cache_create接口中参数指定的值一一赋值到struct kmem_cache结构中。
struct kmem_cache {
// slab cache 的名称, 也就是在 slabinfo 命令中 name 那一列
const char *name;
// 对应参数 size,指 slab 中对象的实际大小,不包含填充的字节数
unsigned int object_size;/* The size of an object without metadata */
// 对象按照指定的 align 进行对齐
unsigned int align;
// slab cache 的管理标志位,用于设置 slab 的一些特性
slab_flags_t flags;
// 池化对象的构造函数,用于创建 slab 对象池中的对象
void (*ctor)(void *);
}
Linux内核在初始化的过程中会提前为内核核心对象创建好对应的slab cache,比如,在内核初始化函数start kernel中调用fork_init函数为struct task_struct创建其所属的salb cache——task_struct_cachep。
// linux_4.1.15\kernel\fork.c
static struct kmem_cache *task_struct_cachep;
void __init fork_init(void)
{
/* create a slab on which task_structs can be allocated */
task_struct_cachep = kmem_cache_create("task_struct", sizeof(struct task_struct),
ARCH_MIN_TASKALIGN, SLAB_PANIC | SLAB_NOTRACK, NULL);
}
接下来看一下kmem_cache_create()函数,该函数定义在linux_4.1.15\mm\slab_common.c中;
struct kmem_cache *kmem_cache_create(const char *name, size_t size, size_t align,
unsigned long flags, void (*ctor)(void *))
{
struct kmem_cache *s;
const char *cache_name;
int err;
/* 获取cpu_hotplug_lock,防止cpu热插拔改变online_cpu_map */
get_online_cpus();
/* 获取mem_hotplug_lock,防止访问内存的时候进行内存热插拔 */
get_online_mems();
/* memory cgroup相关 */
memc