linux內核之slob、slab、slub

Table of Contents

Slob, Slab VS Slub

Overview

slub分配器

Linux slab 分配器剖析

動態內存管理

slab 緩存

slab 背後的動機

API 函數

kmem_cache_create

kmem_cache_destroy

kmem_cache_alloc

kmem_cache_zalloc

kmem_cache_free

kmalloc 和 kfree

其他函數

slab 緩存的示例用法

slab 的 proc 接口

SLOB 分配器

結束語

原文地址:


Slob, Slab VS Slub

Overview

First, "slab" has become a generic name referring to a memory allocation strategy employing an object cache, enabling efficient allocation and deallocation of kernel objects. It was first documented by Sun engineer Jeff Bonwick and implemented in the Solaris 2.4 kernel.1

Linux currently offers three choices for its "slab" allocator :

Slab is the original, based on Bonwick's seminal paper and available since Linux kernel version 2.2. It is a faithful implementation of Bonwick's proposal, augmented by the multiprocessor changes described in Bonwick's follow-up paper2.

Slub is the next-generation replacement memory allocator, which has been the default in the Linux kernel since 2.6.23. It continues to employ the basic "slab" model, but fixes several deficiencies in Slab's design, particularly around systems with large numbers of processors. Slub is simpler than Slab.

SLOB (Simple List Of Blocks) is a memory allocator optimized for embedded systems with very little memory—on the order of megabytes. It applies a very simple first-fit algorithm on a list of blocks, not unlike the old K&R-style heap allocator. In eliminating nearly all of the overhad from the memory allocator, SLOB is a good fit for systems under extreme memory constraints, but it offers none of the benefits described in 1 and can suffer from pathological fragmentation.


slub分配器

作者:itrocker 發佈於:2015-12-21 18:51 分類:內存管理

Linux的物理內存管理採用了以頁爲單位的buddy system(夥伴系統),但是很多情況下,內核僅僅需要一個較小的對象空間,而且這些小塊的空間對於不同對象又是變化的、不可預測的,所以需要一種類似用戶空間堆內存的管理機制(malloc/free)。然而內核對對象的管理又有一定的特殊性,有些對象的訪問非常頻繁,需要採用緩衝機制;對象的組織需要考慮硬件cache的影響;需要考慮多處理器以及NUMA架構的影響。90年代初期,在Solaris 2.4操作系統中,採用了一種稱爲“slab”(原意是大塊的混凝土)的緩衝區分配和管理方法,在相當程度上滿足了內核的特殊需求。

多年以來,SLAB成爲linux kernel對象緩衝區管理的主流算法,甚至長時間沒有人願意去修改,因爲它實在是非常複雜,而且在大多數情況下,它的工作完成的相當不錯。但是,隨着大規模多處理器系統和 NUMA系統的廣泛應用,SLAB 分配器逐漸暴露出自身的嚴重不足:

1). 緩存隊列管理複雜;

2). 管理數據存儲開銷大;

3). 對NUMA支持複雜;

4). 調試調優困難;

5). 摒棄了效果不太明顯的slab着色機制;

針對這些SLAB不足,內核開發人員Christoph Lameter在Linux內核2.6.22版本中引入一種新的解決方案:SLUB分配器。SLUB分配器特點是簡化設計理念,同時保留SLAB分配器的基本思想:每個緩衝區由多個小的slab 組成,每個 slab 包含固定數目的對象。SLUB分配器簡化kmem_cache,slab等相關的管理數據結構,摒棄了SLAB 分配器中衆多的隊列概念,並針對多處理器、NUMA系統進行優化,從而提高了性能和可擴展性並降低了內存的浪費。爲了保證內核其它模塊能夠無縫遷移到SLUB分配器,SLUB還保留了原有SLAB分配器所有的接口API函數。

(注:本文源碼分析基於linux-4.1.x)

整體數據結構關係如下圖所示:

1 SLUB分配器的初始化

SLUB初始化有兩個重要的工作:第一,創建用於申請struct kmem_cache和struct kmem_cache_node的kmem_cache;第二,創建用於常規kmalloc的kmem_cache。

1.1 申請kmem_cache的kmem_cache

第一個工作涉及到一個“先有雞還是先有蛋”的問題,因爲創建kmem_cache需要從kmem_cache的緩衝區申請,而這時候還沒有創建kmem_cache的緩衝區。kernel的解決辦法是先用兩個靜態變量boot_kmem_cache和boot_kmem_cache_node來保存struct kmem_cach和struct kmem_cache_node緩衝區管理數據,以兩個靜態變量爲基礎申請大小爲struct kmem_cache和struct kmem_cache_node對象大小的slub緩衝區,隨後再從這些緩衝區中分別申請兩個kmem_cache,然後把boot_kmem_cache和boot_kmem_cache_node中的內容拷貝到新申請的對象中,從而完成了struct kmem_cache和struct kmem_cache_node管理結構的bootstrap(自引導)。

void __init kmem_cache_init(void)
{
	//聲明靜態變量,存儲臨時kmem_cache管理結構
	static __initdata struct kmem_cache boot_kmem_cache,
		boot_kmem_cache_node; 
•••
	kmem_cache_node = &boot_kmem_cache_node;
	kmem_cache = &boot_kmem_cache;
 
	//申請slub緩衝區,管理數據放在臨時結構中
	create_boot_cache(kmem_cache_node, "kmem_cache_node",
		sizeof(struct kmem_cache_node), SLAB_HWCACHE_ALIGN);
	create_boot_cache(kmem_cache, "kmem_cache",
			offsetof(struct kmem_cache, node) +
				nr_node_ids * sizeof(struct kmem_cache_node *),
		       SLAB_HWCACHE_ALIGN);
 
	//從剛纔掛在臨時結構的緩衝區中申請kmem_cache的kmem_cache,並將管理數據拷貝到新申請的內存中
	kmem_cache = bootstrap(&boot_kmem_cache);
 
	//從剛纔掛在臨時結構的緩衝區中申請kmem_cache_node的kmem_cache,並將管理數據拷貝到新申請的內存中
	kmem_cache_node = bootstrap(&boot_kmem_cache_node);
•••
}

1.2 創建kmalloc常規緩存

原則上系統會爲每個2次冪大小的內存塊申請一個緩存,但是內存塊過小時,會產生很多碎片浪費,所以系統爲96B和192B也各自創建了一個緩存。於是利用了一個size_index數組來幫助確定小於192B的內存塊使用哪個緩存

void __init create_kmalloc_caches(unsigned long flags)
{
•••
	/*使用SLUB時KMALLOC_SHIFT_LOW=3,KMALLOC_SHIFT_HIGH=13
	也就是說使用kmalloc能夠申請的最小內存是8B,最大內存是8KB
	申請內存是向上對其2的n次冪,創建對應大小緩存保存在kmalloc_caches [n]*/
	for (i = KMALLOC_SHIFT_LOW; i <= KMALLOC_SHIFT_HIGH; i++) {
		if (!kmalloc_caches[i]) {
			kmalloc_caches[i] = create_kmalloc_cache(NULL,
							1 << i, flags);
		}
 
		/*
		 * Caches that are not of the two-to-the-power-of size.
		 * These have to be created immediately after the
		 * earlier power of two caches
		 */
		/*有兩個例外,大小爲64~96B和128B~192B,單獨創建了兩個緩存
		保存在kmalloc_caches [1]和kmalloc_caches [2]*/
		if (KMALLOC_MIN_SIZE <= 32 && !kmalloc_caches[1] && i == 6)
			kmalloc_caches[1] = create_kmalloc_cache(NULL, 96, flags);
 
		if (KMALLOC_MIN_SIZE <= 64 && !kmalloc_caches[2] && i == 7)
			kmalloc_caches[2] = create_kmalloc_cache(NULL, 192, flags);
	}
•••
}

緩存的創建與銷燬

2.1 緩存的創建

創建緩存通過接口kmem_cache_create進行,在創建新的緩存以前,嘗試找到可以合併的緩存,合併條件包括對對象大小以及緩存屬性的判斷,如果可以合併則直接返回已存在的kmem_cache,並創建一個kobj鏈接指向同一個節點。

創建新的緩存主要是申請管理結構暫用的空間,並初始化,這些管理結構包括kmem_cache、kmem_cache_nodes、kmem_cache_cpu。同時在sysfs創建kobject節點。最後把kmem_cache加入到全局cahce鏈表slab_caches中。

2.2 緩存的銷燬

銷燬過程比創建過程簡單的多,主要工作是釋放partial隊列所有page,釋放kmem_cache_cpu,釋放每個node的kmem_cache_node,最後釋放kmem_cache本身。

申請對象

對象是SLUB分配器中可分配的內存單元,與SLAB相比,SLUB對象的組織非常簡潔,申請過程更加高效。SLUB沒有任何管理區結構來管理對象,而是將對象之間的關聯嵌入在對象本身的內存中,因爲申請者並不關心對象在分配之前內存的內容是什麼。而且各個SLUB之間的關聯,也利用page自身結構進行處理。

每個CPU都有一個slab作爲本地高速緩存,只要slab所在的node與申請者要求的node匹配,同時該slab還有空閒對象,則直接在cpu_slab中取出空閒對象,否則就進入慢速路徑。

每個對象內存的offset偏移位置都存放着下一個空閒對象,offset通常爲0,也就是複用對象內存的第一個字來保存下一個空閒對象的指針,當滿足條件(flags & (SLAB_DESTROY_BY_RCU | SLAB_POISON)) 或者有對象構造函數時,offset不爲0,每個對象的結構如下圖。

cpu_slab的freelist則保存着當前第一個空閒對象的地址。

如果本地CPU緩存沒有空閒對象,則申請新的slab;如果有空閒對象,但是內存node不相符,則deactive當前cpu_slab,再申請新的slab。

deactivate_slab主要進行兩步工作:

第一步,將cpu_slab的freelist全部釋放回page->freelist;

第二部,根據page(slab)的狀態進行不同操作,如果該slab有部分空閒對象,則將page移到kmem_cache_node的partial隊列;如果該slab全部空閒,則直接釋放該slab;如果該slab全部佔用,而且開啓了CONFIG_SLUB_DEBUG編譯選項,則將page移到full隊列。

page的狀態也從frozen改變爲unfrozen。(frozen代表slab在cpu_slub,unfroze代表在partial隊列或者full隊列)。

申請新的slab有兩種情況,如果cpu_slab的partial隊列不爲空,則取出隊列中下一個page作爲新的cpu_slab,同時再次檢測內存node是否相符,還不相符則循環處理。

如果cpu_slab的partial隊列爲空,則查看本node的partial隊列是否爲空,如果不空,則取出page;如果爲空,則看一定距離範圍內其它node的partial隊列,如果還爲空,則需要創建新slab。

創建新slab其實就是申請對應order的內存頁,用來放足夠數量的對象。值得注意的是其中order以及對象數量的確定,這兩者又是相互影響的。order和object數量同時存放在kmem_cache成員kmem_cache_order_objects中,低16位用於存放object數量,高位存放order。order與object數量的關係非常簡單:((PAGE_SIZE << order) - reserved) / size。

下面重點看calculate_order這個函數

static inline int calculate_order(int size, int reserved)
{
•••
	//嘗試找到order與object數量的最佳配合方案
	//期望的效果就是剩餘的碎片最小
	min_objects = slub_min_objects;
	if (!min_objects)
		min_objects = 4 * (fls(nr_cpu_ids) + 1);
	max_objects = order_objects(slub_max_order, size, reserved);
	min_objects = min(min_objects, max_objects);
	
	//fraction是碎片因子,需要滿足的條件是碎片部分乘以fraction小於slab大小
	// (slab_size - reserved) % size <= slab_size / fraction
	while (min_objects > 1) {
		fraction = 16;
		while (fraction >= 4) {
			order = slab_order(size, min_objects,
					slub_max_order, fraction, reserved);
			if (order <= slub_max_order)
				return order;
	        //放寬條件,容忍的碎片大小增倍
			fraction /= 2;
		}
		min_objects--;
	}
 
	//嘗試一個slab只包含一個對象
	order = slab_order(size, 1, slub_max_order, 1, reserved);
	if (order <= slub_max_order)
		return order;
 
	//使用MAX_ORDER且一個slab只含一個對象
	order = slab_order(size, 1, MAX_ORDER, 1, reserved);
	if (order < MAX_ORDER)
		return order;
	return -ENOSYS;
}

釋放對象

從上面申請對象的流程也可以看出,釋放的object有幾個去處:

1)cpu本地緩存slab,也就是cpu_slab;

2)放回object所在的page(也就是slab)中;另外要處理所在的slab:

2.1)如果放回之後,slab完全爲空,則直接銷燬該slab;

2.2)如果放回之前,slab爲滿,則判斷slab是否已被凍結;如果已凍結,則不需要做其他事;如果未凍結,則將其凍結,放入cpu_slab的partial隊列;如果cpu_slab partial隊列過多,則將隊列中所有slab一次性解凍到各自node的partial隊列中。

值得注意的是cpu partial隊列的功能是個可選項,依賴於內核選項CONFIG_SLUB_CPU_PARTIAL,如果沒有開啓,則不使用cpu partial隊列,直接使用各個node的partial隊列。


Linux slab 分配器剖析

M. Jones,2010 年 9 月 20 日發佈

動態內存管理

內存管理的目標是提供一種方法,爲實現各種目的而在各個用戶之間實現內存共享。內存管理方法應該實現以下兩個功能:

  • 最小化管理內存所需的時間
  • 最大化用於一般應用的可用內存(最小化管理開銷)

內存管理實際上是一種關於權衡的零和遊戲。您可以開發一種使用少量內存進行管理的算法,但是要花費更多時間來管理可用內存。也可以開發一個算法來有效地管理內存,但卻要使用更多的內存。最終,特定應用程序的需求將促使對這種權衡作出選擇。

每個內存管理器都使用了一種基於堆的分配策略。在這種方法中,大塊內存(稱爲 )用來爲用戶定義的目的提供內存。當用戶需要一塊內存時,就請求給自己分配一定大小的內存。堆管理器會查看可用內存的情況(使用特定算法)並返回一塊內存。搜索過程中使用的一些算法有 first-fit(在堆中搜索到的第一個滿足請求的內存塊 )和 best-fit(使用堆中滿足請求的最合適的內存塊)。當用戶使用完內存後,就將內存返回給堆。

這種基於堆的分配策略的根本問題是碎片(fragmentation)。當內存塊被分配後,它們會以不同的順序在不同的時間返回。這樣會在堆中留下一些洞,需要花一些時間纔能有效地管理空閒內存。這種算法通常具有較高的內存使用效率(分配需要的內存),但是卻需要花費更多時間來對堆進行管理。

另外一種方法稱爲 buddy memory allocation,是一種更快的內存分配技術,它將內存劃分爲 2 的冪次方個分區,並使用 best-fit 方法來分配內存請求。當用戶釋放內存時,就會檢查 buddy 塊,查看其相鄰的內存塊是否也已經被釋放。如果是的話,將合併內存塊以最小化內存碎片。這個算法的時間效率更高,但是由於使用 best-fit 方法的緣故,會產生內存浪費。

本文將着重介紹 Linux 內核的內存管理,尤其是 slab 分配提供的機制。

slab 緩存

Linux 所使用的 slab 分配器的基礎是 Jeff Bonwick 爲 SunOS 操作系統首次引入的一種算法。Jeff 的分配器是圍繞對象緩存進行的。在內核中,會爲有限的對象集(例如文件描述符和其他常見結構)分配大量內存。Jeff 發現對內核中普通對象進行初始化所需的時間超過了對其進行分配和釋放所需的時間。因此他的結論是不應該將內存釋放回一個全局的內存池,而是將內存保持爲針對特定目而初始化的狀態。例如,如果內存被分配給了一個互斥鎖,那麼只需在爲互斥鎖首次分配內存時執行一次互斥鎖初始化函數(mutex_init)即可。後續的內存分配不需要執行這個初始化函數,因爲從上次釋放和調用析構之後,它已經處於所需的狀態中了。

Linux slab 分配器使用了這種思想和其他一些思想來構建一個在空間和時間上都具有高效性的內存分配器。

圖 1 給出了 slab 結構的高層組織結構。在最高層是cache_chain,這是一個 slab 緩存的鏈接列表。這對於 best-fit 算法非常有用,可以用來查找最適合所需要的分配大小的緩存(遍歷列表)。cache_chain 的每個元素都是一個kmem_cache 結構的引用(稱爲一個 cache)。它定義了一個要管理的給定大小的對象池。

圖 1. 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 列表中。

slab 背後的動機

與傳統的內存管理模式相比, slab 緩存分配器提供了很多優點。首先,內核通常依賴於對小對象的分配,它們會在系統生命週期內進行無數次分配。slab 緩存分配器通過對類似大小的對象進行緩存而提供這種功能,從而避免了常見的碎片問題。slab 分配器還支持通用對象的初始化,從而避免了爲同一目而對一個對象重複進行初始化。最後,slab 分配器還可以支持硬件緩存對齊和着色,這允許不同緩存中的對象佔用相同的緩存行,從而提高緩存的利用率並獲得更好的性能。

API 函數

現在來看一下能夠創建新 slab 緩存、向緩存中增加內存、銷燬緩存的應用程序接口(API)以及 slab 中對對象進行分配和釋放操作的函數。

第一個步驟是創建 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 所示。

表 1. kmem_cache_create 的部分選項(在 flags 參數中指定)

選項 說明
SLAB_RED_ZONE 在對象頭、尾插入標誌,用來支持對緩衝區溢出的檢查。
SLAB_POISON 使用一種己知模式填充 slab,允許對緩存中的對象進行監視(對象屬對象所有,不過可以在外部進行修改)。
SLAB_HWCACHE_ALIGN 指定緩存對象必須與硬件緩存行對齊。

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 函數。調用者提供了從中分配對象的緩存以及一組標誌:

1

void kmem_cache_alloc( struct kmem_cache *cachep, gfp_t flags );

這個函數從緩存中返回一個對象。注意如果緩存目前爲空,那麼這個函數就會調用 cache_alloc_refill 向緩存中增加內存。 kmem_cache_alloc 的 flags 選項與 kmalloc 的 flags 選項相同。表 2 給出了標誌選項的部分列表。

表 2. kmem_cache_alloc 和 kmalloc 內核函數的標誌選項

標誌 說明
GFP_USER 爲用戶分配內存(這個調用可能會睡眠)。
GFP_KERNEL 從內核 RAM 中分配內存(這個調用可能會睡眠)。
GFP_ATOMIC 使該調用強制處於非睡眠狀態(對中斷處理程序非常有用)。
GFP_HIGHUSER 從高端內存中分配內存。

kmem_cache_zalloc

內核函數 kmem_cache_zalloc 與 kmem_cache_alloc 類似,只不過它對對象執行 memset 操作,用來在將對象返回調用者之前對其進行清除操作。

kmem_cache_free

要將一個對象釋放回 slab,可以使用 kmem_cache_free。調用者提供了緩存引用和要釋放的對象。

1

void kmem_cache_free( struct kmem_cache *cachep, void *objp );

kmalloc 和 kfree

內核中最常用的內存管理函數是 kmalloc 和 kfree 函數。這兩個函數的原型如下:

1

2

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)。

1

2

3

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 );

slab 緩存的示例用法

下面的代碼片斷展示了創建新 slab 緩存、從緩存中分配和釋放對象然後銷燬緩存的過程。首先,必須要定義一個kmem_cache 對象,然後對其進行初始化(請參看清單 1)。這個特定的緩存包含 32 字節的對象,並且是硬件緩存對齊的(由標誌參數 SLAB_HWCACHE_ALIGN 定義)。

清單 1. 創建新 slab 緩存

1

2

3

4

5

6

7

8

9

10

11

12

13

14

static struct kmem_cache *my_cachep;

 

static void init_my_cache( void )

{

 

   my_cachep = kmem_cache_create(

                  "my_cache",            /* Name */

                  32,                    /* Object Size */

                  0,                     /* Alignment */

                  SLAB_HWCACHE_ALIGN,    /* Flags */

                  NULL, NULL );          /* Constructor/Deconstructor */

 

   return;

}

使用所分配的 slab 緩存,您現在可以從中分配一個對象了。清單 2 給出了一個從緩存中分配和釋放對象的例子。它還展示了兩個其他函數的用法。

清單 2. 分配和釋放對象

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

int slab_test( void )

{

  void *object;

 

  printk( "Cache name is %s\n", kmem_cache_name( my_cachep ) );

  printk( "Cache object size is %d\n", kmem_cache_size( my_cachep ) );

 

  object = kmem_cache_alloc( my_cachep, GFP_KERNEL );

 

  if (object) {

 

    kmem_cache_free( my_cachep, object );

 

  }

 

  return 0;

}

最後,清單 3 演示了 slab 緩存的銷燬。調用者必須確保在執行銷燬操作過程中,不要從緩存中分配對象。

清單 3. 銷燬 slab 緩存

1

2

3

4

5

6

7

static void remove_my_cache( void )

{

 

  if (my_cachep) kmem_cache_destroy( my_cachep );

 

  return;

}

slab 的 proc 接口

proc 文件系統提供了一種簡單的方法來監視系統中所有活動的 slab 緩存。這個文件稱爲 /proc/slabinfo,它除了提供一些可以從用戶空間訪問的可調整參數之外,還提供了有關所有 slab 緩存的詳細信息。當前版本的 slabinfo 提供了一個標題,這樣輸出結果就更具可讀性。對於系統中的每個 slab 緩存來說,這個文件提供了對象數量、活動對象數量以及對象大小的信息(除了每個 slab 的對象和頁面之外)。另外還提供了一組可調整的參數和 slab 數據。

要調優特定的 slab 緩存,可以簡單地向 /proc/slabinfo 文件中以字符串的形式迴轉 slab 緩存名稱和 3 個可調整的參數。下面的例子展示瞭如何增加 limit 和 batchcount 的值,而保留 shared factor 不變(格式爲 “cache name limit batchcount shared factor”):

1

# echo "my_cache 128 64 8" > /proc/slabinfo

limit 字段表示每個 CPU 可以緩存的對象的最大數量。batchcount 字段是當緩存爲空時轉換到每個 CPU 緩存中全局緩存對象的最大數量。 shared 參數說明了對稱多處理器(Symmetric MultiProcessing,SMP)系統的共享行爲。

注意您必須具有超級用戶的特權才能在 proc 文件系統中爲 slab 緩存調優參數。

SLOB 分配器

對於小型的嵌入式系統來說,存在一個 slab 模擬層,名爲 SLOB。這個 slab 的替代品在小型嵌入式 Linux 系統中具有優勢,但是即使它保存了 512KB 內存,依然存在碎片和難於擴展的問題。在禁用 CONFIG_SLAB 時,內核會回到這個 SLOB 分配器中。更多信息請參看 參考資料 一節。

結束語

slab 緩存分配器的源代碼實際上是 Linux 內核中可讀性較好的一部分。除了函數調用的間接性之外,源代碼也非常直觀,總的來說,具有很好的註釋。如果您希望瞭解更多有關 slab 緩存分配器的內容,建議您從源代碼開始,因爲它是有關這種機制的最新文檔。 下面的 參考資料 一節提供了介紹 slab 緩存分配器的參考資料,但是不幸的是就目前的 2.6 實現來說,這些文檔都已經過時了。

相關主題

原文地址:

http://wiki.dreamrunner.org/public_html/Embedded-System/kernel/slab-slub-slob.html

http://www.wowotech.net/memory_management/247.html

https://www.ibm.com/developerworks/cn/linux/l-linux-slab-allocator/

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