本文將介紹Linux的slab層,首先我們要解決一個問題就是什么是slab,slab是做什么用的。
在linux內(nèi)核中會有許多小對象,這些對象構(gòu)造銷毀十分頻繁,比如i-node,dentry。這么這些對象如果每次構(gòu)建的時候就向內(nèi)存要一個頁,而其實際大小可能只有幾個字節(jié),這樣就非常浪費,為了解決這個問題就引入了一種新的機(jī)制來處理在同一頁框中如何分配小存儲器區(qū)。這就是我們要討論的slab層。在講述slab前,我想先鋪墊一下有關(guān)內(nèi)存頁的概念,我們都知道在linux中內(nèi)存都是以頁為單位來進(jìn)行管理的(通常為4KB),當(dāng)內(nèi)核需要內(nèi)存就調(diào)用如:kmem_getpages這樣的接口(底層調(diào)用__alloc_pages())。那么內(nèi)核是如何管理頁的分配的,這里linux使用了伙伴算法。slab也是向內(nèi)核申請一個個頁框,然后再對這些頁框做管理來達(dá)到分配小存儲區(qū)的目的的。
slab層主要起到了兩個方面的作用:
1. slab可以對小對象進(jìn)行分配,這樣就不用為每個小對象分配一個頁框,節(jié)省了空間。
2. 內(nèi)核中的一些小對象創(chuàng)建析構(gòu)很頻繁,slab對這些小對象做了緩存,可以重復(fù)利用一些相同的對象,減少內(nèi)存分配次數(shù)。
linux中是通過高速緩存數(shù)據(jù)結(jié)構(gòu)來管理相同大小或者說是相同對象的。比如所有dentry的對象都是在dentry_cache中管理。
高速緩存的數(shù)據(jù)結(jié)構(gòu)是struct kmem_cache_s。它由以下幾部分組成:
1. 三個不同類型的slab組成的鏈表
分別代表沒有空閑,部分空閑和全部空閑的slab。當(dāng)請求一個對象時,會先從slabs_partial中的slab中分配對象,如果slabs_partial為空,就去slabs_free中去找(然后將這個slab放到slabs_partial中)。
2. 對象的大小和對象的個數(shù)
我們先介紹下什么是slab描述符,它代表了什么:
slab在這里就是對一組相同大小的對象進(jìn)行管理的容器,它的struct類型是slab_t
既然是對對象進(jìn)行管理,那么就有一個內(nèi)存的指針指向它所管理的對象:void* s_mem。并且它還需要記錄哪些對象被使用了,哪些是空閑的。這時就需要有一個數(shù)組記錄這個東西,這就是free變量。你可能會奇怪它的類型是一個unsigned int:
它是如何記錄這個數(shù)組的呢?事實上是記錄在slab描述符后面的數(shù)組中,這個數(shù)組中的每一個元素記錄著下一個空閑的內(nèi)存對象的位置。而free記錄的就是第一個空閑的對象。這樣就可以從free開始遍歷所有空閑的對象。
下圖顯示了slab對象的內(nèi)存分布,這里需要解釋一下的是slab descriptor會根據(jù)情況被存放在當(dāng)前slab的內(nèi)存中或者存放在通用的高速緩存中。
slab分配器把對象分組放進(jìn)高速緩存中,每個高速緩存都是同種類型對象的一種“儲備”,例如,當(dāng)一個文件被打開時,存放相應(yīng)“打開文件”對象所需的存儲器區(qū)就是從一個叫做filp的高速緩存中得到的。
那我們先來說下高速緩存描述符: struct kmem_cache_s,這是用來管理一組相同大小的對象的。它有三個由slab組成的鏈表:
struct list_head slabs_full;
struct list_head slabs_partial;
struct list_head slabs_free;
分別代表沒有空閑,部分空閑和全部空閑的slab,比如
cache_cache高速緩存的高速緩存
cache_sizes普通高速緩存
這里有一篇文章重點介紹slab的用法: https://www.ibm.com/developerworks/cn/linux/l-linux-slab-allocator/
這里簡單介紹下linux內(nèi)核中的slab的接口:
1. 創(chuàng)建slab緩存結(jié)構(gòu)
struct kemem_cache* my_cachep;
kmem_cache_create/kmem_cache_destroy
struct kmem_cache *
kmem_cache_create( const char *name, size_t size, size_t align,
unsigned long flags;
void (*ctor)(void*, struct kmem_cache *, unsigned long),
void (*dtor)(void*, struct kmem_cache *, unsigned long));