SLAB分配器小结

在遇到类似于越界溢出、迷途指针这些漏洞时,一般会对内存进行覆盖(比如喷堆),无论哪种重写方式,无疑都需要了解内存分配器的工作方式。在进行了阅读,源码的分析,包括很多博文的帮助下大概摸清了SLAB分配器的脉络。

SLAB分配器主要的作用是针对常用的对象,以链表的方式缓存它们,缓存的链表会连续的存放,这样频繁地分配和回收就不会导致内存碎片,此外对于很多初始化的对象(如互斥锁内存回收后仍保持初始化的状态,再次分配的时候只需要将数据写入就可以了,不需要再次初始化。


在SLAB层的设计中,将不同的对象划分成高速缓存组kmem_cache,每个kmem_cache存放不同类型的对象,kmem_cache之间用链表连接。这些高速缓存保存SLAB的链表,每个SLAB结构由一个或多个页page组成,通常为一个。在页里用来保存对象的实例。

结构图如下:



每个缓存都有三种SLAB:

slabs_full:完全分配的 slab
slabs_partial:部分分配的 slab
slabs_free:空 slab,或者没有对象被分配

结合图片和源码,看看SLAB关键结构的实现


struct kmem_cache{
/* 1) per-cpu data, touched during every alloc/free */
	struct array_cache *array[NR_CPUS];//array是一个指向数组的指针,每个数组项都对应于系统中的一个CPU。每个数组项都包含了另一个指针,指向下文讨论的array_cache结构的实例
/* 2) Cache tunables. Protected by cache_chain_mutex */
	unsigned int batchcount;//指定了在每CPU列表为空的情况下,从缓存的slab中获取对象的数目。它还表示在缓存增长时分配的对象数目
	unsigned int limit;//limit指定了每CPU列表中保存的对象的最大数目,如果超出该值,内核会将batchcount个对象返回到slab
	unsigned int shared;

	unsigned int buffer_size;//指定了缓存中管理的对象的长度
	u32 reciprocal_buffer_size;//buffer_size的倒数值,为了克服出发运算对性能的影响
/* 3) touched by every alloc & free from the backend */

	unsigned int flags;//是一个标志寄存器,定义缓存的全局性质,当前只有一个标志位,用于标记slab头得管理数据是在slab内还是外
	unsigned int num;//保存了可以放入slab的对象的最大数目

/* 4) cache_grow/shrink */
	/* order of pgs per slab (2^n) */
	unsigned int gfporder;//指定了slab包含的页数目以2为底得对数

	/* force GFP flags, e.g. GFP_DMA */
	gfp_t gfpflags;//与伙伴系统交互时所提供的分配标识

	size_t colour;//指定了颜色的最大数目
	unsigned int colour_off;//是基本偏移量乘以颜色值获得的绝对偏移量
	struct kmem_cache *slabp_cache;//如果slab头部的管理数据存储在slab外部,则slabp_cache指向分配所需内存的一般性缓存;如果slab头部在slab上,则其为NULL
	unsigned int slab_size;//slab管理区的大小
	unsigned int dflags;//另一个标志集合,描述slab的“动态性质”,但目前还没有定义标志
	/* constructor func */
	void (*ctor)(struct kmem_cache *, void *);//创建高速缓存时的构造函数指针
/* 5) cache creation/removal */
	const char *name;//缓存的名称
	struct list_head next;//用于将kmem_cache的所有实例保存在全局链表cache_chain上

/* 6) statistics */
#if STATS//统计数据字段
	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;
#endif
#if DEBUG
	/*
	 * If debugging is enabled, then the allocator can add additional
	 * fields and/or padding to every object. buffer_size contains the total
	 * object size including these internal fields, the following two
	 * variables contain the offset to the user object and its size.
	 */
	int obj_offset;
	int obj_size;
#endif
	/*
	 * We put nodelists[] at the end of kmem_cache, because we want to size
	 * this array to nr_node_ids slots instead of MAX_NUMNODES
	 * (see kmem_cache_init())
	 * We still use [MAX_NUMNODES] and not [1] or [0] because cache_cache
	 * is statically defined, so we reserve the max number of nodes.
	 */
	struct kmem_list3 *nodelists[MAX_NUMNODES];//nodelists是一个数组,每个数组对应于系统中一个可能的内存结点。每个数组项都包含kmem_list3的一个实例
	/*
	 * Do not add fields after nodelists[]
	 */
}
</pre><pre name="code" class="cpp">struct kmem_list3 {
	struct list_head slabs_partial;//部分空闲的slab链表
	struct list_head slabs_full;//非空闲的slab链表
	struct list_head slabs_free;//完全空闲的slab链表
	unsigned long free_objects;//部分空闲的slab链表和完全空闲的slab链表中空闲对象的总数
	unsigned int free_limit;//指定了所有slab上容许未使用对象的最大数目
	unsigned int colour_next;//内核建立的下一个slab的颜色
	spinlock_t list_lock;
	struct array_cache *shared;//结点内共享
	struct array_cache **alien;//在其他结点上
	unsigned long next_reap;//定义了内核在两次尝试收缩缓存之间,必须经过的时间间隔
	int free_touched;//表示缓存是否是活动的
};
struct slab {
	struct list_head list;
	unsigned long colouroff;//该Slab上着色区的大小
	void *s_mem;//指向对象区的起点
	unsigned int inuse;//Slab中所分配对象的个数
	kmem_bufctl_t free;//指明了空闲对象链中的第一个对象,kmem_bufctl_t其实是一个整数
	unsigned short nodeid;//结点标识号
};
struct array_cache {
	unsigned int avail;//本地高速缓存中可用的空闲对象数
	unsigned int limit;//空闲对象的上限
	unsigned int batchcount;//一次转入和转出的对象数量
	unsigned int touched;//标识本地CPU最近是否被使用
	spinlock_t lock;
	void *entry[];//这是一个伪数组,便于对后面用于跟踪空闲对象的指针数组的访问
};
还要介绍的一个数据结构就是struct array_cache。struct kmem_cache中定义了一个struct array_cache指针数组,数组的元素个数对应了系统的CPU数,和伙伴系统中的每CPU页框高速缓存类似,该结构用来描述每个CPU的本地高速缓存,它可以减少SMP系统中对于自旋锁的竞争。在每个array_cache的末端都用一个指针数组记录了slab中的空闲对象,分配对象时,采用LIFO方式,也就是将该数组中的最后一个索引对应的对象分配出去,以保证该对象还驻留在高速缓存中的可能性。实际上,每次分配内存都是直接与本地CPU高速缓存进行交互,只有当其空闲内存不足时,才会从kmem_list中的slab中引入一部分对象到本地高速缓存中,而kmem_list中的空闲对象也不足了,那么就要从伙伴系统中引入新的页来建立新的slab了,这一点也和伙伴系统的每CPU页框高速缓存很类似。 上述内容来自http://blog.csdn.net/vanbreaker/article/details/7664296
在SLAB结构中,还存在一种叫做着色区的结构。由于对象实例保存在页中,为了对象起始地址与缓冲行对齐,设置着色区对对象的位置进行调整和补充


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章