内存管理之slab分配器
基本思想
? ? ? ? 與傳統的內存管理模式相比, slab 緩存分配器提供了很多優點。首先,內核通常依賴于對小對象的分配,它們會在系統生命周期內進行無數次分配。slab 緩存分配器通過對類似大小的對象進行緩存而提供這種功能,從而避免了常見的碎片問題。slab 分配器還支持通用對象的初始化,從而避免了為同一目而對一個對象重復進行初始化。最后,slab 分配器還可以支持硬件緩存對齊和著色,這允許不同緩存中的對象占用相同的緩存行,從而提高緩存的利用率并獲得更好的性能。
說明:
? ? ? 每個緩存都包含了一個 slabs 列表,這是一段連續的內存塊(通常都是頁面)。存在 3 種 slab:
? ? ??? ? ??slabs_full - ?完全分配的 slab
? ? ??? ? ??slabs_partial -?部分分配的 slab
? ? ??? ? ??slabs_empty -?空 slab,或者沒有對象被分配
? ? ??slabs_empty 列表中的 slab 是進行回收(reaping)的主要備選對象。正是通過此過程,slab 所使用的內存被返回給操作系統供其他用戶使用。
? ? ??slab 列表中的每個 slab 都是一個連續的內存塊(一個或多個連續頁),它們被劃分成一個個對象。這些對象是從特定緩存中進行分配和釋放的基本元素。注意 slab 是 slab 分配器進行操作的最小分配單位,因此如果需要對 slab 進行擴展,這也就是所擴展的最小值。通常來說,每個 slab 被分配為多個對象。
? ? ??由于對象是從 slab 中進行分配和釋放的,因此單個 slab 可以在 slab 列表之間進行移動。例如,當一個 slab 中的所有對象都被使用完時,就從 slabs_partial 列表中移動到 slabs_full 列表中。當一個 slab 完全被分配并且有對象被釋放后,就從 slabs_full 列表中移動到 slabs_partial 列表中。當所有對象都被釋放之后,就從 slabs_partial 列表移動到 slabs_empty 列表中。
API 函數
http://www.ibm.com/developerworks/cn/linux/l-linux-slab-allocator/index.html
? ? ??第一個步驟是創建 slab 緩存結構,您可以將其靜態創建為:
struct struct kmem_cache *my_cachep;? ? ? 然后其他 slab 緩存函數將使用該引用進行創建、刪除、分配等操作。kmem_cache 結構包含了每個中央處理器單元(CPU)的數據、一組可調整的(可以通過 proc 文件系統訪問)參數、統計信息和管理 slab 緩存所必須的元素。
kmem_cache_create
? ? ??內核函數 kmem_cache_create 用來創建一個新緩存。這通常是在內核初始化時執行的,或者在首次加載內核模塊時執行。其原型定義如下: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));? ? ? ? ? ? name 參數定義了緩存名稱,proc 文件系統(在 /proc/slabinfo 中)使用它標識這個緩存。
? ? ??? ? ??size 參數指定了為這個緩存創建的對象的大小, align 參數定義了每個對象必需的對齊。 flags 參數指定了為緩存啟用的選項。這些標志如表 1 所示。
? ? ??? ? ??ctor 和 dtor 參數定義了一個可選的對象構造器和析構器。構造器和析構器是用戶提供的回調函數。當從緩存中分配新對象時,可以通過構造器進行初始化。
在創建緩存之后, kmem_cache_create 函數會返回對它的引用。注意這個函數并沒有向緩存分配任何內存。相反,在試圖從緩存(最初為空)分配對象時,refill 操作將內存分配給它。當所有對象都被使用掉時,也可以通過相同的操作向緩存添加內存。
kmem_cache_destroy
? ? ??內核函數 kmem_cache_destroy 用來銷毀緩存。這個調用是由內核模塊在被卸載時執行的。在調用這個函數時,緩存必須為空。void kmem_cache_destroy( struct kmem_cache *cachep );
kmem_cache_alloc
? ? ??要從一個命名的緩存中分配一個對象,可以使用 kmem_cache_alloc 函數。調用者提供了從中分配對象的緩存以及一組標志:void kmem_cache_alloc( struct kmem_cache *cachep, gfp_t flags );? ? ? 這個函數從緩存中返回一個對象。注意如果緩存目前為空,那么這個函數就會調用 cache_alloc_refill 向緩存中增加內存。 kmem_cache_alloc 的 flags 選項與 kmalloc 的 flags 選項相同。 給出了標志選項的部分列表。
kmem_cache_zalloc
? ? ??內核函數 kmem_cache_zalloc 與 kmem_cache_alloc 類似,只不過它對對象執行 memset 操作,用來在將對象返回調用者之前對其進行清除操作。kmem_cache_free
? ? ??要將一個對象釋放回 slab,可以使用 kmem_cache_free。調用者提供了緩存引用和要釋放的對象。void kmem_cache_free( struct kmem_cache *cachep, void *objp );
kmalloc 和 kfree
? ? ??內核中最常用的內存管理函數是 kmalloc 和 kfree 函數。這兩個函數的原型如下:void *kmalloc( size_t size, int flags ); void kfree( const void *objp );? ? ? 注意在 kmalloc 中,惟一兩個參數是要分配的對象的大小和一組標志(請參看 表 2 中的部分列表)。但是 kmalloc 和 kfree 使用了類似于前面定義的函數的 slab 緩存。kmalloc 沒有為要從中分配對象的某個 slab 緩存命名,而是循環遍歷可用緩存來查找可以滿足大小限制的緩存。找到之后,就(使用 __kmem_cache_alloc)分配一個對象。要使用 kfree 釋放對象,從中分配對象的緩存可以通過調用 virt_to_cache 確定。這個函數會返回一個緩存引用,然后在 __cache_free 調用中使用該引用釋放對象。
其他函數
? ? ??slab 緩存 API 還提供了其他一些非常有用的函數。 kmem_cache_size 函數會返回這個緩存所管理的對象的大小。您也可以通過調用 kmem_cache_name 來檢索給定緩存的名稱(在創建緩存時定義)。緩存可以通過釋放其中的空閑 slab 進行收縮。這可以通過調用 kmem_cache_shrink 實現。注意這個操作(稱為回收)是由內核定期自動執行的(通過 kswapd)。unsigned int kmem_cache_size( struct kmem_cache *cachep ); const char *kmem_cache_name( struct kmem_cache *cachep ); int kmem_cache_shrink( struct kmem_cache *cachep );
轉載于:https://www.cnblogs.com/youngerchina/p/5624454.html
總結
以上是生活随笔為你收集整理的内存管理之slab分配器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《Linux4.0设备驱动开发详解》笔记
- 下一篇: 容器技术的落地还要依靠SDN