slab内存池的设计与实现

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值