1. 程式人生 > >Linux Slab分配器(一)--概述

Linux Slab分配器(一)--概述

       slab分配器是Linux記憶體管理中非常重要和複雜的一部分,其工作是針對一些經常分配並釋放的物件,如程序描述符等,這些物件的大小一般比較小,如果直接採用夥伴系統來進行分配和釋放,不僅會造成大量的內碎片,而且處理速度也太慢。而slab分配器是基於物件進行管理的,相同型別的物件歸為一類(如程序描述符就是一類),每當要申請這樣一個物件,slab分配器就從一個slab列表中分配一個這樣大小的單元出去,而當要釋放時,將其重新儲存在該列表中,而不是直接返回給夥伴系統。slab分配物件時,會使用最近釋放的物件記憶體塊,因此其駐留在CPU快取記憶體的概率較高。

       用於描述和管理cache的資料結構是struct kmem_cache

struct kmem_cache {
/* 1) per-cpu data, touched during every alloc/free */
    /*per-CPU資料,記錄了本地快取記憶體的資訊,也用於跟蹤最近釋放的物件,每次分配和釋放都要直接訪問它*/
	struct array_cache *array[NR_CPUS]; 
/* 2) Cache tunables. Protected by cache_chain_mutex */
	unsigned int batchcount;  /*本地快取記憶體轉入或轉出的大批物件數量*/
	unsigned int limit;       /*本地快取記憶體中空閒物件的最大數目*/
	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;          /* 快取記憶體的永久標識*/
	unsigned int num;		  /* 一個slab所包含的物件數目 */

/* 4) cache_grow/shrink */
	/* order of pgs per slab (2^n) */
	unsigned int gfporder;   /*一個slab包含的連續頁框數的對數*/
 
	/* force GFP flags, e.g. GFP_DMA */
	gfp_t gfpflags;          /*與夥伴系統互動時所提供的分配標識*/

	size_t colour;	       /* 顏色的個數*/
	unsigned int colour_off; /* 著色的偏移量 */
	
	/*如果將slab描述符儲存在外部,該指標指向儲存slab描述符的cache,
	  否則為NULL*/
	struct kmem_cache *slabp_cache;
	unsigned int slab_size;  /*slab管理區的大小*/
	unsigned int dflags;     /*動態標識*/

	/* constructor func */
	void (*ctor)(void *obj); /*建立快取記憶體時的建構函式指標*/

/* 5) cache creation/removal */
	const char *name;         /*快取記憶體名*/
	struct list_head next;    /*用於將快取記憶體鏈入cache chain*/

/* 6) statistics */
#ifdef CONFIG_DEBUG_SLAB /*一些用於除錯用的變數*/
	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;

	/*
	 * 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 /* CONFIG_DEBUG_SLAB */

	/*
	 * 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用於組織該快取記憶體中的slab*/
	struct kmem_list3 *nodelists[MAX_NUMNODES];
	/*
	 * Do not add fields after nodelists[]
	 */
};
struct kmem_list3 {
	struct list_head slabs_partial;/*slab連結串列,包含空閒物件和已分配物件的slab描述符*/
	struct list_head slabs_full;   /*slab連結串列,只包含非空閒的slab描述符*/
	struct list_head slabs_free;   /*slab連結串列,只包含空閒的slab描述符*/
	unsigned long free_objects;    /*快取記憶體中空閒物件的個數*/
	unsigned int free_limit;       /*空閒物件的上限*/
	unsigned int colour_next;	    /*下一個slab使用的顏色*/
	spinlock_t list_lock;
	struct array_cache *shared;	/* shared per node */
	struct array_cache **alien;	/* on other nodes */
	unsigned long next_reap;	/* updated without locking */
	int free_touched;		/* updated without locking */
};


描述和管理單個slab的結構是struct slab

struct slab {
	struct list_head list;  /*用於將slab鏈入kmem_list3的連結串列*/
	unsigned long colouroff;/*該slab的著色偏移*/
	void *s_mem;            /*指向slab中的第一個物件*/
	unsigned int inuse;     /*已分配出去的物件*/
	kmem_bufctl_t free;     /*下一個空閒物件的下標*/
	unsigned short nodeid;  /*節點標識號*/
};

還要介紹的一個數據結構就是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頁框快取記憶體很類似。

struct array_cache {
	unsigned int avail;/*本地快取記憶體中可用的空閒物件數*/
	unsigned int limit;/*空閒物件的上限*/
	unsigned int batchcount;/*一次轉入和轉出的物件數量*/
	unsigned int touched;   /*標識本地CPU最近是否被使用*/
	spinlock_t lock;
	void *entry[];	/*這是一個偽陣列,便於對後面用於跟蹤空閒物件的指標陣列的訪問
			 * Must have this definition in here for the proper
			 * alignment of array_cache. Also simplifies accessing
			 * the entries.
			 */
};


slab分配器涉及到了一些繁雜的概念,這些在後面再逐一結合程式碼進行講解,在理解slab分配器的工作之前,必須先理解上述這些資料結構之間的聯絡,下圖給出了一個清晰的描述