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結構中,還存在一種叫做着色區的結構。由於對象實例保存在頁中,爲了對象起始地址與緩衝行對齊,設置着色區對對象的位置進行調整和補充


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