這還是暑假之前寫的總結(jié)... 這幾天一個kernel群里老有人問關(guān)于slab方面的問題... 所以就在這里把些的總結(jié)貼一下... 獻丑了...
Slab Allocator 邏輯結(jié)構(gòu)如下圖所示:
如圖,主要結(jié)構(gòu)包括 cache 以及 slabs。故相應(yīng)的數(shù)據(jù)結(jié)構(gòu)有 cache 描述符和 slab 描述符。
cache 描述符
struct kmem_cache_s {
/* full, partial first, then free */
struct list_head slabs_full;
struct list_head slabs_partial;
struct list_head slabs_free;
unsigned int objsize;
unsigned int flags; /* constant flags */
unsigned int num; /* # of objs per slab */
spinlock_t spinlock;
#ifdef CONFIG_SMP
unsigned int batchcount;
#endif
unsigned int gfporder;
unsigned int gfpflags;
size_t colour; /* cache colouring range */
unsigned int colour_off; /* colour offset */
unsigned int colour_next; /* cache colouring */
kmem_cache_t *slabp_cache;
unsigned int growing;
unsigned int dflags; /* dynamic flags */
/* constructor func */
void (*ctor)(void *, kmem_cache_t *, unsigned long);
/* de-constructor func */
void (*dtor)(void *, kmem_cache_t *, unsigned long);
unsigned long failures;
char name[CACHE_NAMELEN];
struct list_head next;
#ifdef CONFIG_SMP
cpucache_t *cpudata[NR_CPUS];
#endif
#if STATS
unsigned long num_active;
unsigned long num_allocations;
unsigned long high_mark;
unsigned long grown;
unsigned long reaped;
unsigned long errors;
#ifdef CONFIG_SMP
atomic_t allochit;
atomic_t allocmiss;
atomic_t freehit;
atomic_t freemiss;
#endif
#endif
};
各個字段含義如下:
slab_* 保存slabs的鏈表,如上圖,full、partial和empty。
objsize 裝入緩存器中的每個”對象”的大小。
flags 決定當(dāng)處理這個緩存器時,分配器的部分行為。見8.1.2
num 每個slab包含的”對象”的數(shù)量
spinlock 保護這個結(jié)構(gòu)的自旋鎖,防止并發(fā)訪問
batchcount 決定對于每個CPU的緩存器,一次所分配的”對象”的數(shù)量
gfporder 表示slab的頁面的數(shù)量。每slab有2^gfporder個頁面。因為這
是伙伴分配器所提供的分配大小。
gfpflags 保存當(dāng)調(diào)用伙伴分配器分配頁面時所用到GFP標(biāo)識。
colour 每個slab會盡可能的將”對象”保存到不同的高速緩存行。緩存器著色
會在8.1.5節(jié)深入討論。
colour_off 保持slabs對齊到的字節(jié)數(shù)。例如,對于size-X緩存器里的slab會對齊到
L1高速緩存。
colour_next 下一個要使用的顏色行。當(dāng)這個值達到colour時就會反繞到0。
growing 設(shè)置這個標(biāo)識可以表示這個緩存器的大小是否增加了。如果增加了,
則在內(nèi)存緊張時,選擇它來獲取空閑的slabs的可能性會更小。
dflags 在緩存器存在期間會改變的動態(tài)標(biāo)識。
ctor 一個復(fù)雜的”對象”可以可選的提供一個構(gòu)造函數(shù)來初始化每個新
的”對象”。這是指向那個函數(shù)的指針,而且可以是NULL。
dtor 析構(gòu)函數(shù)的指針,可以是NULL。
failure 在代碼中不在使用這個字段,因此都初始化為0。
name 每個緩存器的名稱
next 緩存器鏈表上的下一個緩存器。
cpudata 每CPU數(shù)據(jù),指向描述一小組可用”對象”的管理結(jié)構(gòu)。
num_active 在緩存器中當(dāng)前活躍的”對象”的數(shù)量
num_allocations 在緩存器中已經(jīng)分配了的”對象”的數(shù)量的總數(shù)。
high_mark num_active能夠達到的最大的值
grown kmem_cache_grow()被調(diào)用的次數(shù)
reaped 緩存器被釋放的次數(shù)
allochit 分配時,從每CPU緩存中分配”對象”的次數(shù)
allocmiss 與allochit相反
freehit 將”對象”釋放到每CPU緩存中的次數(shù)
freemiss 與freehit相反。
這個結(jié)構(gòu)相當(dāng)?shù)拇?Slab Allocator中對每種”對象”都有一個對應(yīng)的cache。每個cache管理
多個slab。Slab才是真正存放”對象”的地方。管理這些”對象”的結(jié)構(gòu)就是slab描述符。
Slab 描述符
typedef struct slab_s {
struct list_head list;
unsigned long colouroff;
void *s_mem; /* including colour offset */
unsigned int inuse; /* num of objs active in slab */
kmem_bufctl_t free;
} slab_t;
各個字段含義如下:
list 此 slab 所屬于的鏈表。這個字段是在緩存器中的 slab_full 或者
slab_patial 或者 slab_free 上。
colouroff 這是離在 slab 中的第一個”對象”的基址的著色偏移值。 第一個對象的
地址是 s_mem + colouroff。
s_mem 給出在 slab 中第一個”對象”的起始地址。
inuse 在 slab 中的活躍”對象”的個數(shù)。
free 存儲空閑”對象”的 bufctls 數(shù)組。參見 8.2.3 中的詳細(xì)介紹。
這個結(jié)構(gòu)僅僅是用來管理”對象”的,在創(chuàng)建 slab 時需要分配存放”對象”的內(nèi)存,所需內(nèi)存
從 buddy allocator 中申請,內(nèi)存大小為其所屬 cache 中 gfporder 指定。顯然,所申請的內(nèi)
存是連續(xù)的 2^cachep->gfporder 個頁面。Slab 描述符可以存放在這個所分配的內(nèi)存起始
處,也可以放在另外的地方。如圖:
通用緩存器組
typedef struct cache_sizes {
size_t cs_size;
kmem_cache_t *cs_cachep;
kmem_cache_t *cs_dmacachep;
} cache_sizes_t;
各字段含義如下:
cs_size 這個通用緩存器的大小
cs_cachep 用于在普通內(nèi)存中分配”對象”的緩存器指針
cs_dmacachep 用于在 DMA 內(nèi)存中分配”對象”的緩存器指針
在系統(tǒng)中靜態(tài)定義了這個類型的一個數(shù)組,其主要為大小不定的一些通用結(jié)構(gòu)分配內(nèi)存。
例如,如過 Slab 描述符的位置是 Off_Slab,則這個 Slab 描述符以及其后的 kmem_bufctl_t[]
就放在這個通用緩存器組中的某一個緩存器里。
下面所要將到的數(shù)據(jù)結(jié)構(gòu)是針對 SMP 系統(tǒng)進行優(yōu)化所用到的數(shù)據(jù)結(jié)構(gòu):
每 CPU cache
typedef struct cpucache_s {
unsigned int avail;
unsigned int limit;
} cpucache_t;
avail 在這個 cpucache 上空閑”對象”的數(shù)量。
limit 可以存在的空閑”對象”的總數(shù)。
每個 CPU 都有一個對應(yīng)的小的本地 cache,這個 cache 并不是上面所提到的 cache 描述符。
這個 cache 僅僅是特定于某個 cache 描述符所管理的”對象”的數(shù)組。如 mm_struct 相關(guān)
的 cache 描述符所管理的”對象”就是 mm_struct 類型的,則這個 CPU 的本地 cache 就是
mm_struct 類型的數(shù)組。在為某 CPU 分配 cpucache_t 結(jié)構(gòu)時,除了分配 cpucache_t 結(jié)構(gòu)
所需要的內(nèi)存外,也分配 limit*sizeof(void *)字節(jié)的內(nèi)存。當(dāng)然,它們是連續(xù)的。
如代碼所示:
ccnew = kmalloc(sizeof(void*)*limit+
sizeof(cpucache_t), GFP_KERNEL);
ccnew 就是 cpucache_t *類型。
因此 cpucache_t 結(jié)構(gòu)后面就是 limit 個 void 類型的指針。 所以 avail 還有一個作用就是作
為空閑”對象”的索引。如在從這個 CPU 本地 cache 分配一個”對象”時就有如下代碼:
obj = cc_entry(cc)[--cc->avail];
其中:
#define cc_entry(cpucache) \
((void **)(((cpucache_t*)(cpucache))+1))
這個代碼就可以解釋上面所說的一切。
更新每 CPU cache 信息所用到的數(shù)據(jù)結(jié)構(gòu)
typedef struct ccupdate_struct_s
{
kmem_cache_t *cachep;
cpucache_t *new[NR_CPUS];
} ccupdate_struct_t;
cachep 是正在被更新的緩存器的指針,new 是系統(tǒng)中每個 CPU 的 cpucache 描述符的數(shù)組。
當(dāng)然僅有在 SMP 系統(tǒng)上設(shè)置 SMP 選項時才可用。
在每 CPU cache 信息改變之后,在更新每個 CPU cache 信息時就不需要考慮并發(fā)性帶來
的一致性問題,因為每個 CPU 只為從對應(yīng)的 ccpudata_struct_t->new[ID]中獲取新信息來
更新自己本地的 cache。這樣就不需要用自旋鎖來保護。