偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

解密Slab分配器:內(nèi)存管理的高效武器

系統(tǒng) Linux
Slab 分配器是 Linux 內(nèi)核中一種極為重要的內(nèi)存管理機(jī)制,主要用于高效管理小塊內(nèi)存的分配。它針對(duì)頻繁分配和釋放的小對(duì)象進(jìn)行了專(zhuān)門(mén)優(yōu)化,能夠有效減少內(nèi)存碎片,顯著提高系統(tǒng)性能。

在 Linux 操作系統(tǒng)的神秘世界里,內(nèi)存管理如同一場(chǎng)精密的棋局,每一步都關(guān)乎著系統(tǒng)的性能和穩(wěn)定性。而在這場(chǎng)棋局中,Slab 分配器宛如一顆璀璨的明珠,是內(nèi)存管理的高效武器。它以其獨(dú)特的設(shè)計(jì)和強(qiáng)大的功能,為 Linux 系統(tǒng)的內(nèi)存分配與釋放提供了卓越的解決方案?,F(xiàn)在,讓我們一同揭開(kāi) Slab 分配器的神秘面紗,探索它在內(nèi)存管理中的神奇之處。

Linux內(nèi)核版本:5.0
架構(gòu):ARM64
Linux 5.0內(nèi)核源碼注釋倉(cāng)庫(kù)地址:
zhangzihengya/LinuxSourceCode_v5.0_study (http://github.com)

一、Slab分配器概述

Slab 分配器是 Linux 內(nèi)核中一種極為重要的內(nèi)存管理機(jī)制,主要用于高效管理小塊內(nèi)存的分配。它針對(duì)頻繁分配和釋放的小對(duì)象進(jìn)行了專(zhuān)門(mén)優(yōu)化,能夠有效減少內(nèi)存碎片,顯著提高系統(tǒng)性能。在 Linux 內(nèi)核的眾多子系統(tǒng)中,Slab 分配器得到了廣泛應(yīng)用,比如網(wǎng)絡(luò)緩沖區(qū)、文件系統(tǒng)緩存以及進(jìn)程控制塊等領(lǐng)域。

Slab 分配器的核心概念包括 Cache(緩存)、Slab 和 Object。其中,Cache 是為每種類(lèi)型的對(duì)象創(chuàng)建的緩存,每個(gè)緩存存儲(chǔ)相同大小的對(duì)象集合。Slab 則是一塊連續(xù)的內(nèi)存區(qū)域,用于存儲(chǔ)一組特定大小的對(duì)象。而 Object 是在 Slab 中實(shí)際存儲(chǔ)的數(shù)據(jù)單元。

Slab 分配器具有諸多優(yōu)勢(shì)。首先,它通過(guò)對(duì)象復(fù)用,避免了頻繁的分配與釋放操作,極大地提高了內(nèi)存分配的效率。其次,它能夠保證內(nèi)存分配尺寸和對(duì)齊一致,從而有效減少內(nèi)存碎片問(wèn)題。最后,Slab 分配器適用于不同大小的對(duì)象,能夠顯著提升內(nèi)核的整體性能。

通過(guò)查看/proc/slabinfo文件,可以了解 Slab 分配器的內(nèi)存使用情況,幫助調(diào)優(yōu)系統(tǒng)性能。該文件輸出的頭部包含多個(gè)字段,其中name字段表示 slab 緩存的名稱(chēng),每個(gè) slab 緩存存儲(chǔ)相同類(lèi)型和大小的對(duì)象;active_objs字段表示當(dāng)前在使用的(已分配的)對(duì)象數(shù)量;num_objs字段表示緩存中分配的對(duì)象總數(shù),包括已經(jīng)分配和空閑的對(duì)象;objsize字段表示每個(gè)對(duì)象的大小(以字節(jié)為單位);objperslab字段表示每個(gè) slab 中包含的對(duì)象數(shù)量;pagesperslab字段表示每個(gè) slab 使用的頁(yè)數(shù);tunables字段中的值控制 slab 緩存的行為,如limit表示 slab 緩存中每個(gè) CPU 可以緩存的最大對(duì)象數(shù)量,batchcount表示每次從全局緩存到 CPU 本地緩存中批量獲取的對(duì)象數(shù),sharedfactor控制多個(gè) CPU 是否共享 slab 緩存;slabdata部分包含有關(guān) slab 使用的統(tǒng)計(jì)數(shù)據(jù),如active_slabs表示當(dāng)前正在使用的 slab 數(shù)量,num_slabs表示系統(tǒng)中分配的總 slab 數(shù)量,sharedavail表示 CPU 本地緩存中可用對(duì)象的數(shù)量。

二、核心技術(shù)

2.1 slab機(jī)制

slab分配器最終還使用伙伴系統(tǒng)來(lái)分配實(shí)際的物理頁(yè)面,只不過(guò)slab分配器在這些連續(xù)的物理頁(yè)面上實(shí)現(xiàn)了自己的機(jī)制,以此來(lái)對(duì)小內(nèi)存塊進(jìn)行管理。slab機(jī)制如下圖所示:

圖片圖片

其中每個(gè)slab描述符都會(huì)建立共享對(duì)象緩沖池和本地對(duì)象緩沖池。slab機(jī)制有如下特性:

把分配的內(nèi)存塊當(dāng)作對(duì)象(object)來(lái)看待。對(duì)象可以自定義構(gòu)造函數(shù)(constructor) 和析構(gòu)函數(shù)(destructor)來(lái)初始化對(duì)象的內(nèi)容并釋放對(duì)象的內(nèi)容。

slab對(duì)象被釋放之后不會(huì)馬上丟棄而是繼續(xù)保留在內(nèi)存中,可能稍后會(huì)被用到,這樣不需要重新向伙伴系統(tǒng)申請(qǐng)內(nèi)存。

slab機(jī)制可以根據(jù)特定大小的內(nèi)存塊來(lái)創(chuàng)建slab描述符,如內(nèi)存中常見(jiàn)的數(shù)據(jù)結(jié)構(gòu)、打開(kāi)文件對(duì)象等,這樣可以有效地避免內(nèi)存碎片的產(chǎn)生,也可以快速獲得頻繁訪(fǎng)問(wèn)的數(shù)據(jù)結(jié)構(gòu)。另外,slab機(jī)制也支持按2的n次方字節(jié)大小分配內(nèi)存塊。

slab機(jī)制創(chuàng)建了多層的緩沖池,充分利用了空間換時(shí)間的思想,未雨綢謬,有效地解決了效率問(wèn)題。

每個(gè)CPU有本地對(duì)象緩沖池,避免了多核之間的鎖爭(zhēng)用問(wèn)題。

每個(gè)內(nèi)存節(jié)點(diǎn)有共享對(duì)象緩沖池。

2.2 slab框架

為了更好地理解slab分配器的細(xì)節(jié),我們先從宏觀(guān)上大致了解下slab系統(tǒng)的架構(gòu),如下圖所示:

圖片圖片

slab系統(tǒng)由slab描述符、slab節(jié)點(diǎn)、本地對(duì)象緩沖池、共享對(duì)象緩沖池、3個(gè)slab鏈表、n個(gè)slab分配器,以及眾多slab緩存對(duì)象組成,相關(guān)數(shù)據(jù)結(jié)構(gòu)的注解如下。slab描述符:

// kmem_cache數(shù)據(jù)結(jié)構(gòu)是 slab 分配器中的核心成員,每個(gè) slab 描述符都用一個(gè) kmem_cache 數(shù)據(jù)結(jié)構(gòu)來(lái)抽象描述
struct kmem_cache {
	// Per-cpu 變量的 array_cache 數(shù)據(jù)結(jié)構(gòu),每個(gè)CPU一個(gè),表示本地 CPU 的對(duì)象緩沖池
	struct array_cache __percpu *cpu_cache;

/* 1) Cache tunables. Protected by slab_mutex */
	// 表示在當(dāng)前 CPU 的本地對(duì)象緩沖池 array_cache 為空時(shí),從共享對(duì)象緩沖池或 slabs_partial/slabs_free 列表中獲取的對(duì)象的數(shù)目
	unsigned int batchcount;
	// 當(dāng)本地對(duì)象緩沖池中的空閑對(duì)象的數(shù)目大于 limit 時(shí),會(huì)主動(dòng)釋放 batchcount 個(gè)對(duì)象,便于內(nèi)核回收和銷(xiāo)毀 slab
	unsigned int limit;
	// 用于多核系統(tǒng)
	unsigned int shared;

	// 對(duì)象的長(zhǎng)度,這個(gè)長(zhǎng)度要加上 align 對(duì)齊字節(jié)
	unsigned int size;
	struct reciprocal_value reciprocal_buffer_size;
/* 2) touched by every alloc & free from the backend */

	// 對(duì)象的分配掩碼
	slab_flags_t flags;		/* constant flags */
	// 一個(gè) slab 中最多有多少個(gè)對(duì)象
	unsigned int num;		/* # of objs per slab */

/* 3) cache_grow/shrink */
	/* order of pgs per slab (2^n) */
	unsigned int gfporder;

	/* force GFP flags, e.g. GFP_DMA */
	gfp_t allocflags;

	// 一個(gè) slab 中可以有多少個(gè)不同的緩存行
	size_t colour;			/* cache colouring range */
	// 著色區(qū)的長(zhǎng)度,和 L1 緩存行大小相同
	unsigned int colour_off;	/* colour offset */
	struct kmem_cache *freelist_cache;
	// 每個(gè)對(duì)象要占用 1 字節(jié)來(lái)存放 freelist
	unsigned int freelist_size;

	/* constructor func */
	void (*ctor)(void *obj);

/* 4) cache creation/removal */
	// slab 描述符的名稱(chēng)
	const char *name;
	struct list_head list;
	int refcount;
	// 對(duì)象的實(shí)際大小
	int object_size;
	// 對(duì)齊的長(zhǎng)度
	int align;

...

	// slab 節(jié)點(diǎn)
	// 在 NUMA 系統(tǒng)中,每個(gè)節(jié)點(diǎn)有一個(gè) kmem_cache_node 數(shù)據(jù)結(jié)構(gòu)
	// 在 ARM Vexpress 平臺(tái)上,只有一個(gè)節(jié)點(diǎn)
	struct kmem_cache_node *node[MAX_NUMNODES];
};

slab節(jié)點(diǎn):

struct kmem_cache_node {
	// 用于保護(hù) slab 節(jié)點(diǎn)中的 slab 鏈表
	spinlock_t list_lock;

#ifdef CONFIG_SLAB
	// slab 鏈表,表示 slab 節(jié)點(diǎn)中有部分空閑對(duì)象
	struct list_head slabs_partial;	/* partial list first, better asm code */
	// slab 鏈表,表示 slab 節(jié)點(diǎn)中沒(méi)有空閑對(duì)象
	struct list_head slabs_full;
	// slab 鏈表,表示 slab 節(jié)點(diǎn)中全部都是空閑對(duì)象
	struct list_head slabs_free;
	// 表示 slab 節(jié)點(diǎn)中有多少個(gè) slab 對(duì)象
	unsigned long total_slabs;	/* length of all slab lists */
	// 表示 slab 節(jié)點(diǎn)中有多少個(gè)全是空閑對(duì)象的 slab 對(duì)象
	unsigned long free_slabs;	/* length of free slab list only */
	// 空閑對(duì)象的數(shù)目
	unsigned long free_objects;
	// 表示 slab 節(jié)點(diǎn)中所有空閑對(duì)象的最大閾值,即 slab 節(jié)點(diǎn)中可容許的空閑對(duì)象數(shù)目最大閾值
	unsigned int free_limit;
	// 記錄當(dāng)前著色區(qū)的編號(hào)。所有 slab 節(jié)點(diǎn)都按照著色編號(hào)來(lái)計(jì)算著色區(qū)的大小,達(dá)到最大值后又從 0 開(kāi)始計(jì)算
	unsigned int colour_next;	/* Per-node cache coloring */
	// 共享對(duì)象緩沖區(qū)。在多核 CPU 中,除了本地 CPU 外,slab 節(jié)點(diǎn)中還有一個(gè)所有 CPU 都共享的對(duì)象緩沖區(qū)
	struct array_cache *shared;	/* shared per node */
	// 用于 NUMA 系統(tǒng)
	struct alien_cache **alien;	/* on other nodes */
	// 下一次收割 slab 節(jié)點(diǎn)的時(shí)間
	unsigned long next_reap;	/* updated without locking */
	// 表示訪(fǎng)問(wèn)了 slabs_free 的 slab 節(jié)點(diǎn)
	int free_touched;		/* updated without locking */
#endif

...

};

對(duì)象緩沖池:

// slab 描述符會(huì)給每個(gè) CPU 提供一個(gè)對(duì)象緩沖池(array_cache)
// array_cache 可以描述本地對(duì)象緩沖池,也可以描述共享對(duì)象緩沖池
struct array_cache {
	// 對(duì)象緩沖池中可用對(duì)象的數(shù)目
	unsigned int avail;
	// 對(duì)象緩沖池中可用對(duì)象數(shù)目的最大閾值
	unsigned int limit;
	// 遷移對(duì)象的數(shù)目,如從共享對(duì)象緩沖池或者其他 slab 中遷移空閑對(duì)象到該對(duì)象緩沖池的數(shù)量
	unsigned int batchcount;
	// 從緩沖池中移除一個(gè)對(duì)象時(shí),將 touched 置為 1 ;
	// 當(dāng)收縮緩沖池時(shí),將 touched 置為 0;
	unsigned int touched;
	// 保存對(duì)象的實(shí)體
	// 指向存儲(chǔ)對(duì)象的變長(zhǎng)數(shù)組,每一個(gè)成員存放一個(gè)對(duì)象的指針。這個(gè)數(shù)組最初最多有 limit 個(gè)成員
	void *entry[];
};

對(duì)象緩沖池的數(shù)據(jù)結(jié)構(gòu)中采用了GCC編譯器的零長(zhǎng)數(shù)組,entry[]數(shù)組用于存放多個(gè)對(duì)象,如下圖所示:

圖片圖片

⑴Cache(緩存)

Slab 分配器中的緩存(Cache)扮演著關(guān)鍵的角色。它是為每種類(lèi)型的對(duì)象專(zhuān)門(mén)創(chuàng)建的,每個(gè)緩存都存儲(chǔ)著相同大小的對(duì)象集合。比如,對(duì)于特定大小的內(nèi)核數(shù)據(jù)結(jié)構(gòu),會(huì)有對(duì)應(yīng)的緩存來(lái)管理這些對(duì)象的分配和釋放。這種設(shè)計(jì)使得在需要分配相同類(lèi)型的對(duì)象時(shí),可以快速?gòu)木彺嬷蝎@取,提高了內(nèi)存分配的效率。

⑵Slab

Slab 是一塊連續(xù)的內(nèi)存區(qū)域,用于存儲(chǔ)一組特定大小的對(duì)象。每個(gè) Slab 都經(jīng)過(guò)精心劃分,以適應(yīng)特定對(duì)象的存儲(chǔ)需求。Slab 的大小通常由所存儲(chǔ)對(duì)象的大小和數(shù)量決定。當(dāng)系統(tǒng)需要為特定類(lèi)型的對(duì)象分配內(nèi)存時(shí),Slab 分配器會(huì)從合適的 Slab 中分配空間。如果沒(méi)有合適的 Slab 可用,分配器可能會(huì)創(chuàng)建新的 Slab。

⑶Object(對(duì)象)

Object 是在 Slab 中實(shí)際存儲(chǔ)的數(shù)據(jù)單元。每個(gè)對(duì)象代表著特定類(lèi)型的數(shù)據(jù)結(jié)構(gòu)或資源。例如,在 Linux 內(nèi)核中,進(jìn)程控制塊(PCB)可以作為一個(gè)對(duì)象存儲(chǔ)在 Slab 中。對(duì)象可以處于不同的狀態(tài),如已分配、空閑或部分空閑。空閑對(duì)象通過(guò)空閑鏈表進(jìn)行跟蹤,以便在需要分配新對(duì)象時(shí)能夠快速找到可用的空間。

三、優(yōu)勢(shì)解析

3.1 對(duì)象復(fù)用

Slab 分配器通過(guò)緩存對(duì)象避免了頻繁的分配與釋放操作。在內(nèi)核中,會(huì)為有限的對(duì)象集分配大量?jī)?nèi)存,例如文件描述符和其他常見(jiàn)結(jié)構(gòu)。而 Slab 分配器圍繞對(duì)象緩存進(jìn)行,將內(nèi)存保持為針對(duì)特定目的而初始化的狀態(tài)。例如,如果內(nèi)存被分配給了一個(gè)互斥鎖,那么只需在為互斥鎖首次分配內(nèi)存時(shí)執(zhí)行一次互斥鎖初始化函數(shù)即可。后續(xù)的內(nèi)存分配不需要執(zhí)行這個(gè)初始化函數(shù),因?yàn)閺纳洗吾尫藕驼{(diào)用析構(gòu)之后,它已經(jīng)處于所需的狀態(tài)中了。這樣,當(dāng)系統(tǒng)再次需要相同類(lèi)型的對(duì)象時(shí),可以直接從緩存中獲取,極大地提高了內(nèi)存分配的效率。

3.2 減少內(nèi)存碎片

Slab 分配器能夠保證內(nèi)存分配尺寸和對(duì)齊一致,從而有效減少內(nèi)存碎片問(wèn)題。每個(gè)緩存結(jié)構(gòu)都包括了兩個(gè)重要的成員:nodelists 和 array。nodelists 中的 kmem_list3 結(jié)構(gòu)將 slab 分為完全用盡的 slab 鏈表、部分用盡的 slab 鏈表和空閑的 slab 鏈表。部分空閑的 slab 在最開(kāi)始,當(dāng)一個(gè) slab 中的所有對(duì)象都被使用完時(shí),就從 slabs_partial 列表中移動(dòng)到 slabs_full 列表中。當(dāng)一個(gè) slab 完全被分配并且有對(duì)象被釋放后,就從 slabs_full 列表中移動(dòng)到 slabs_partial 列表中。當(dāng)所有對(duì)象都被釋放之后,就從 slabs_partial 列表移動(dòng)到 slabs_empty 列表中。這種管理方式使得內(nèi)存的分配和釋放更加有序,減少了內(nèi)存碎片的產(chǎn)生。

此外,對(duì)象在 slab 中不是連續(xù)排列的,為了滿(mǎn)足對(duì)齊要求,會(huì)在 slab 對(duì)象中添加填充字節(jié)以滿(mǎn)足對(duì)齊要求,使用對(duì)齊的地址可以加速內(nèi)存訪(fǎng)問(wèn)。如果創(chuàng)建 slab 時(shí)指定了 SLAB_HWCACHE_ALIGN 標(biāo)志,則會(huì)按照 cache_line_size 的返回值對(duì)齊,即對(duì)齊的硬件緩存行上。如果對(duì)象小于硬件緩存行的一半,則將多個(gè)對(duì)象放入一個(gè)緩存行。如果沒(méi)有指定對(duì)齊標(biāo)記,則對(duì)齊到 BYTES_PER_WORD,即對(duì)齊到 void 指針?biāo)枳止?jié)數(shù)目。

3.2 高效的內(nèi)存管理

Slab 分配器適用于不同大小的對(duì)象,能夠顯著提升內(nèi)核的整體性能。Slab 分配器把對(duì)象分組放進(jìn)高速緩存,每個(gè)高速緩存都是同種類(lèi)型對(duì)象的一種 “儲(chǔ)備”。一個(gè) cache 管理一組大小固定的內(nèi)存塊,每個(gè)內(nèi)存塊都可用作一種數(shù)據(jù)結(jié)構(gòu)。cache 中的內(nèi)存塊來(lái)自一到多個(gè) slab,一個(gè) slab 來(lái)自物理內(nèi)存管理器的一到多個(gè)物理頁(yè),該 slab 被分成一組固定大小的塊,被稱(chēng)為 slab 對(duì)象。

與傳統(tǒng)的內(nèi)存管理模式相比,Slab 緩存分配器提供了很多優(yōu)點(diǎn)。首先,內(nèi)核通常依賴(lài)于對(duì)小對(duì)象的分配,它們會(huì)在系統(tǒng)生命周期內(nèi)進(jìn)行無(wú)數(shù)次分配。Slab 緩存分配器通過(guò)對(duì)類(lèi)似大小的對(duì)象進(jìn)行緩存而提供這種功能,從而避免了常見(jiàn)的碎片問(wèn)題。Slab 分配器還支持通用對(duì)象的初始化,從而避免了為同一目的而對(duì)一個(gè)對(duì)象重復(fù)進(jìn)行初始化。最后,Slab 分配器還可以支持硬件緩存對(duì)齊和著色,這允許不同緩存中的對(duì)象占用相同的緩存行,從而提高緩存的利用率并獲得更好的性能。

四、關(guān)鍵結(jié)構(gòu)

4.1 kmem_cache

kmem_cache定義了要管理的給定大小的對(duì)象池,是 Linux 內(nèi)存管理中 Slab 分配器的核心結(jié)構(gòu)之一。它包含多個(gè)重要參數(shù)和引用,對(duì)內(nèi)存分配起著關(guān)鍵作用。

kmem_cache結(jié)構(gòu)中的struct array_cache __percpu *cpu_cache是一個(gè)重要的成員,它是每個(gè) CPU 的對(duì)象緩存池,相當(dāng)于快表。當(dāng)系統(tǒng)進(jìn)行內(nèi)存分配時(shí),會(huì)優(yōu)先從這個(gè)本地緩存中獲取對(duì)象,提高分配速度。

此外,batchcount、limit、shared等參數(shù)分別控制著從共享緩存或其他列表獲取對(duì)象的數(shù)量、本地緩存中空閑對(duì)象的最大數(shù)量以及多核系統(tǒng)中的共享設(shè)置。size參數(shù)表示要管理的對(duì)象的長(zhǎng)度,這個(gè)長(zhǎng)度需要加上對(duì)齊字節(jié)。flags是對(duì)象的分配掩碼,num表示一個(gè) slab 中最多可以有多少個(gè)對(duì)象。

gfporder參數(shù)決定了一個(gè) slab 中占用的連續(xù)頁(yè)框數(shù)的對(duì)數(shù),而allocflags則是與伙伴系統(tǒng)交互時(shí)提供的分配標(biāo)識(shí)。colour和colour_off參數(shù)用于控制 slab 的顏色設(shè)置,實(shí)現(xiàn)緩存著色以提高緩存命中率。freelist_cache和freelist_size在 off-slab 時(shí)使用,將 freelist 放在 slab 物理頁(yè)面外部。

ctor是構(gòu)造函數(shù)指針,用于在創(chuàng)建對(duì)象時(shí)進(jìn)行初始化操作。name是 slab 描述符的名稱(chēng),list用于將該結(jié)構(gòu)鏈接到全局鏈表中,refcount是引用次數(shù),在釋放 slab 描述符時(shí)會(huì)判斷,只有引用次數(shù)為 0 時(shí)才真正釋放。object_size是對(duì)象的實(shí)際大小,align是對(duì)齊的長(zhǎng)度。

4.2 array_cache

array_cache是每個(gè) CPU 的對(duì)象緩存池,在 Slab 分配器中起著實(shí)現(xiàn)快速分配和減少操作的關(guān)鍵作用。

array_cache結(jié)構(gòu)中的avail表示對(duì)象緩存池中可用的對(duì)象數(shù)目。limit和batchcount與kmem_cache中的語(yǔ)義一致,分別控制著緩存的上限和從共享緩存或其他列表獲取對(duì)象的數(shù)量。touched參數(shù)在從緩存池移除一個(gè)對(duì)象時(shí)置 1,而收縮緩存時(shí)置 0。entry數(shù)組保存著對(duì)象的實(shí)體,采用 LIFO(后進(jìn)先出)方式進(jìn)行分配,即將該數(shù)組中的最后一個(gè)索引對(duì)應(yīng)的對(duì)象分配出去,以保證該對(duì)象還駐留在高速緩存中的可能性。

4.3 kmem_cache_node

kmem_cache_node管理從伙伴系統(tǒng)分配的物理頁(yè)面,是 Slab 分配器在 NUMA 架構(gòu)下的重要組成部分。它包含多個(gè) slab 鏈表,對(duì)內(nèi)存的分配和回收進(jìn)行精細(xì)管理。

kmem_cache_node結(jié)構(gòu)中的spinlock_t list_lock用于保護(hù)鏈表操作的互斥。在支持 CONFIG_SLAB 的情況下,它包含slabs_partial、slabs_full和slabs_free三個(gè)鏈表,分別對(duì)應(yīng)部分用盡的 slab 鏈表、完全用盡的 slab 鏈表和空閑的 slab 鏈表。total_slabs表示三個(gè)鏈表中所有 slab 的總數(shù),free_slabs表示空閑 slab 的數(shù)量,free_objects表示三個(gè)鏈表中所有空閑對(duì)象數(shù)目,free_limit表示 slab 中可以容許的空閑對(duì)象數(shù)目最大閾值。colour_next用于控制每個(gè)節(jié)點(diǎn)的緩存著色。

shared是多核 CPU 中的共享緩存區(qū) slab 對(duì)象的指針。當(dāng)一個(gè) slab 中的所有對(duì)象都被使用完時(shí),就從slabs_partial列表中移動(dòng)到slabs_full列表中。當(dāng)一個(gè) slab 完全被分配并且有對(duì)象被釋放后,就從slabs_full列表中移動(dòng)到slabs_partial列表中。當(dāng)所有對(duì)象都被釋放之后,就從slabs_partial列表移動(dòng)到slabs_empty列表中。這種管理方式使得內(nèi)存的分配和釋放更加有序,減少了內(nèi)存碎片的產(chǎn)生。

五、操作流程

5.1 創(chuàng)建 slab 緩存

使用kmem_cache_create函數(shù)創(chuàng)建一個(gè)描述特定對(duì)象類(lèi)型內(nèi)存池的結(jié)構(gòu)。kmem_cache_create函數(shù)需要多個(gè)參數(shù),包括可讀的名稱(chēng)、被管理對(duì)象以字節(jié)計(jì)的長(zhǎng)度、對(duì)齊數(shù)據(jù)時(shí)使用的偏移量、一組標(biāo)志以及構(gòu)造函數(shù)等。首先,對(duì)象長(zhǎng)度會(huì)向上舍入到處理器字長(zhǎng)的倍數(shù)。如果設(shè)置了SLAB_HWCACHE_ALIGN標(biāo)志,內(nèi)核會(huì)按照特定于體系結(jié)構(gòu)的函數(shù)cache_line_size給出的值來(lái)對(duì)齊數(shù)據(jù),并嘗試將盡可能多的對(duì)象填充到一個(gè)緩存行中。如果對(duì)象長(zhǎng)度大于頁(yè)幀的 1/8,則將頭部管理數(shù)據(jù)存儲(chǔ)在 slab 之外,否則存儲(chǔ)在 slab 上。最后,通過(guò)迭代過(guò)程找到理想的 slab 長(zhǎng)度,并對(duì) slab 進(jìn)行著色。

圖片圖片

為了使讀者有更真切的理解,下文將根據(jù)流程圖圍繞源代碼進(jìn)行講解這個(gè)過(guò)程:

kmem_cache_create

// 創(chuàng)建 slab 描述符
// kmem_cache_create() 函數(shù)用于創(chuàng)建自己的緩存描述符;kmalloc() 函數(shù)用于創(chuàng)建通用的緩存
// name:slab 描述符的名稱(chēng)
// size:緩沖對(duì)象的大小
// align:緩沖對(duì)象需要對(duì)齊的字節(jié)數(shù)
// flags:分配掩碼
// ctor:對(duì)象的構(gòu)造函數(shù)
struct kmem_cache *
kmem_cache_create(const char *name, unsigned int size, unsigned int align,
		slab_flags_t flags, void (*ctor)(void *))

kmem_cache_create->...->__kmem_cache_create

// 創(chuàng)建 slab 緩存描述符
int __kmem_cache_create(struct kmem_cache *cachep, slab_flags_t flags)
{
    ...
	// 讓 slab 描述符的大小和系統(tǒng)的 word 長(zhǎng)度對(duì)齊(BYTES_PER_WORD)
	// 當(dāng)創(chuàng)建的 slab 描述符的 size 小于 word 長(zhǎng)度時(shí),slab 分配器會(huì)最終按 word 長(zhǎng)度來(lái)創(chuàng)建
	size = ALIGN(size, BYTES_PER_WORD);

	// SLAB_RED_ZONE 檢查是否溢出,實(shí)現(xiàn)調(diào)試功能
	if (flags & SLAB_RED_ZONE) {
		ralign = REDZONE_ALIGN;
		size = ALIGN(size, REDZONE_ALIGN);
	}

	/* 3) caller mandated alignment */
	// 調(diào)用方強(qiáng)制對(duì)齊
	if (ralign < cachep->align) {
		ralign = cachep->align;
	}
	...
	 * 4) Store it.
	 */
	cachep->align = ralign;
	// colour_off 表示一個(gè)著色區(qū)的長(zhǎng)度,它和 L1 高速緩存行大小相同
	cachep->colour_off = cache_line_size();
	/* Offset must be a multiple of the alignment. */
	if (cachep->colour_off < cachep->align)
		cachep->colour_off = cachep->align;

	// 枚舉類(lèi)型 slab_state 用來(lái)表示 slab 系統(tǒng)中的狀態(tài),如 DOWN、PARTIAL、PARTIAL_NODE、UP 和 FULL 等。當(dāng) slab 機(jī)制完全初始化完成后狀態(tài)變成 FULL
	// slab_is_available() 表示當(dāng) slab 分配器處于 UP 或者 FULL 狀態(tài)時(shí),分配掩碼可以使用 GFP_KERNEL;否則,只能使用 GFP_NOWAIT
	if (slab_is_available())
		gfp = GFP_KERNEL;
	else
		gfp = GFP_NOWAIT;

...

	// slab 對(duì)象的大小按照 cachep->align 大小來(lái)對(duì)齊
	size = ALIGN(size, cachep->align);
	
    ...

	// 若數(shù)組 freelist 小于一個(gè) slab 對(duì)象的大小并且沒(méi)有指定構(gòu)造函數(shù),那么 slab 分配器就可以采用 OBJFREELIST_SLAB 模式
	if (set_objfreelist_slab_cache(cachep, size, flags)) {
		flags |= CFLGS_OBJFREELIST_SLAB;
		goto done;
	}

	// 若一個(gè) slab 分配器的剩余空間小于 freelist 數(shù)組的大小,那么使用 OFF_SLAB 模式
	if (set_off_slab_cache(cachep, size, flags)) {
		flags |= CFLGS_OFF_SLAB;
		goto done;
	}

	// 若一個(gè) slab 分配器的剩余空間大于 slab 管理數(shù)組大小,那么使用正常模式
	if (set_on_slab_cache(cachep, size, flags))
		goto done;

	return -E2BIG;

done:
	// freelist_size 表示一個(gè) slab 分配器中管理區(qū)————freelist 大小
	cachep->freelist_size = cachep->num * sizeof(freelist_idx_t);
	cachep->flags = flags;
	cachep->allocflags = __GFP_COMP;
	if (flags & SLAB_CACHE_DMA)
		cachep->allocflags |= GFP_DMA;
	if (flags & SLAB_RECLAIM_ACCOUNT)
		cachep->allocflags |= __GFP_RECLAIMABLE;
	// size 表示一個(gè) slab 對(duì)象的大小
	cachep->size = size;
	cachep->reciprocal_buffer_size = reciprocal_value(size);

...

	// 繼續(xù)配置 slab 描述符
	err = setup_cpu_cache(cachep, gfp);
	if (err) {
		__kmem_cache_release(cachep);
		return err;
	}

	return 0;
}

5.2 分配內(nèi)存

通過(guò)kmem_cache_alloc從已創(chuàng)建的 slab 緩存中分配內(nèi)存。首先會(huì)從每個(gè) CPU 的本地對(duì)象緩存池(array_cache)中獲取對(duì)象,如果本地緩存為空,則從共享緩存或其他列表中獲取。如果所有列表中都沒(méi)有空閑對(duì)象,則會(huì)調(diào)用cache_grow函數(shù)創(chuàng)建新的 slab。kmem_cache_alloc() 函數(shù)的流程圖如下所示:

圖片圖片

為了使讀者有更真切的理解,下文將根據(jù)流程圖圍繞源代碼進(jìn)行講解這個(gè)過(guò)程:kmem_cache_alloc->slab_alloc

// slab_alloc() 函數(shù)在 slab 對(duì)象分配過(guò)程中是全程關(guān)閉本地中斷的
static __always_inline void *
slab_alloc(struct kmem_cache *cachep, gfp_t flags, unsigned long caller)
{
	...
    local_irq_save(save_flags);
	// 獲取 slab 對(duì)象
	objp = __do_cache_alloc(cachep, flags);
	local_irq_restore(save_flags);
	...

	// 如果分配時(shí)設(shè)置了 __GFP_ZERO 標(biāo)志位,那么使用 memset() 把 slab 對(duì)象的內(nèi)容清零
	if (unlikely(flags & __GFP_ZERO) && objp)
		memset(objp, 0, cachep->object_size);

	slab_post_alloc_hook(cachep, flags, 1, &objp);
	return objp;
}

kmem_cache_alloc->slab_alloc->...->____cache_alloc

// 獲取 slab 對(duì)象
static inline void *____cache_alloc(struct kmem_cache *cachep, gfp_t flags)
{
	void *objp;
	struct array_cache *ac;

	check_irq_off();

	// 獲取 slab 描述符 cachep 中的本地對(duì)象緩沖池 ac
	ac = cpu_cache_get(cachep);
	// 判斷本地對(duì)象緩沖池中有沒(méi)有空閑的對(duì)象
	if (likely(ac->avail)) {
		ac->touched = 1;
		// 獲取 slab 對(duì)象
		objp = ac->entry[--ac->avail];

		STATS_INC_ALLOCHIT(cachep);
		goto out;
	}

	STATS_INC_ALLOCMISS(cachep);
	// 第一次分配緩存對(duì)象時(shí) ac->avail 值為 0,所以它應(yīng)該在 cache_alloc_refill() 函數(shù)中
	objp = cache_alloc_refill(cachep, flags);
	...
    
	return objp;
}

kmem_cache_alloc->slab_alloc->__do_cache_alloc->____cache_alloc->cache_alloc_refill

static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags)
{
	...
	
	// 獲取本地對(duì)象緩沖池 ac
	ac = cpu_cache_get(cachep);
	...
	// 獲取 slab 節(jié)點(diǎn)
	n = get_node(cachep, node);

	BUG_ON(ac->avail > 0 || !n);
	// shared 表示共享對(duì)象緩沖池
	shared = READ_ONCE(n->shared);
	// 若 slab 節(jié)點(diǎn)沒(méi)有空閑對(duì)象并且共享對(duì)象緩沖池 shared 為空或者共享對(duì)象緩沖池里也沒(méi)有空閑對(duì)象,那么直接跳轉(zhuǎn)到 direct_grow 標(biāo)簽處
	if (!n->free_objects && (!shared || !shared->avail))
		goto direct_grow;

	...
	
	// 若共享對(duì)象緩沖池里有空閑對(duì)象,那么嘗試遷移 batchcount 個(gè)空閑對(duì)象到本地對(duì)象緩沖池 ac 中
	// transfer_objects() 函數(shù)用于從共享對(duì)象緩沖池遷移空閑對(duì)象到本地對(duì)象緩沖池
	if (shared && transfer_objects(ac, shared, batchcount)) {
		shared->touched = 1;
		goto alloc_done;
	}

	while (batchcount > 0) {
		/* Get slab alloc is to come from. */
		// 如果共享對(duì)象緩沖池中沒(méi)有空閑對(duì)象,那么 get_first_slab() 函數(shù)會(huì)查看 slab 節(jié)點(diǎn)中的 slabs_partial 鏈表和 slabs_free 鏈表
		page = get_first_slab(n, false);
		if (!page)
			goto must_grow;

		check_spinlock_acquired(cachep);

		// 從 slab 分配器中遷移 batchcount 個(gè)空閑對(duì)象到本地對(duì)象緩沖池中
		batchcount = alloc_block(cachep, ac, page, batchcount);
		fixup_slab_list(cachep, n, page, &list);
	}

must_grow:
	// 更新 slab 節(jié)點(diǎn)中的 free_objects 計(jì)數(shù)值
	n->free_objects -= ac->avail;
alloc_done:
	spin_unlock(&n->list_lock);
	fixup_objfreelist_debug(cachep, &list);

// 表示 slab 節(jié)點(diǎn)沒(méi)有空閑對(duì)象并且共享對(duì)象緩沖池中也沒(méi)有空閑對(duì)象,這說(shuō)明整個(gè)內(nèi)存節(jié)點(diǎn)里沒(méi)有 slab 空閑對(duì)象
// 這種情況下只能重新分配 slab 分配器,這就是一開(kāi)始初始化和配置 slab 描述符的情景
direct_grow:
	if (unlikely(!ac->avail)) {
		/* Check if we can use obj in pfmemalloc slab */
		if (sk_memalloc_socks()) {
			void *obj = cache_alloc_pfmemalloc(cachep, n, flags);

			if (obj)
				return obj;
		}

		// 分配一個(gè) slab 分配器
		page = cache_grow_begin(cachep, gfp_exact_node(flags), node);

		/*
		 * cache_grow_begin() can reenable interrupts,
		 * then ac could change.
		 */
		ac = cpu_cache_get(cachep);
		if (!ac->avail && page)
			// 從剛分配的 slab 分配器的空閑對(duì)象中遷移 batchcount 個(gè)空閑對(duì)象到本地對(duì)象緩沖池中
			alloc_block(cachep, ac, page, batchcount);
		// 把剛分配的 slab 分配器添加到合適的隊(duì)列中,這個(gè)場(chǎng)景下應(yīng)該添加到 slabs_partial 鏈表中
		cache_grow_end(cachep, page);

		if (!ac->avail)
			return NULL;
	}
	// 設(shè)置本地對(duì)象緩沖池的 touched 為 1,表示剛剛使用過(guò)本地對(duì)象緩沖池
	ac->touched = 1;

	// 返回一個(gè)空閑對(duì)象
	return ac->entry[--ac->avail];
}

5.3 釋放內(nèi)存

使用kmem_cache_free釋放內(nèi)存。如果 per-CPU 緩存中的對(duì)象數(shù)目低于允許的限制,則在其中存儲(chǔ)一個(gè)指向緩存中對(duì)象的指針。否則,將一些對(duì)象從緩存移回 slab,并將剩余的對(duì)象向數(shù)組起始處移動(dòng)。如果在刪除之后,slab 中的所有對(duì)象都是未使用的,且緩存中空閑對(duì)象的數(shù)目超過(guò)預(yù)定義的限制,則使用slab_destroy將整個(gè) slab 返回給伙伴系統(tǒng)。如果 slab 同時(shí)包含使用和未使用對(duì)象,則插入到slabs_partial鏈表。如果要銷(xiāo)毀只包含未使用對(duì)象的一個(gè)緩存,則必須調(diào)用kmem_cache_destroy函數(shù)。該流程如下所示:

圖片圖片

為了使讀者有更真切的理解,下文將根據(jù)流程圖圍繞源代碼進(jìn)行講解這個(gè)過(guò)程:kmem_cache_free->__cache_free->___cache_free

void ___cache_free(struct kmem_cache *cachep, void *objp,
		unsigned long caller)
{
	struct array_cache *ac = cpu_cache_get(cachep);
	...
	// 當(dāng)本地對(duì)象緩沖池的空閑對(duì)象數(shù)量 ac->avail 大于或等于 ac->limit 閾值時(shí),就會(huì)調(diào)用 cache_flusharray() 做刷新動(dòng)作,嘗試回收空閑對(duì)象
	if (ac->avail < ac->limit) {
		STATS_INC_FREEHIT(cachep);
	} else {
		STATS_INC_FREEMISS(cachep);
		// 主要用于回收 slab 分配器
		cache_flusharray(cachep, ac);
	}
	...
	// 把對(duì)象釋放到本地對(duì)象緩沖池 ac 中
	ac->entry[ac->avail++] = objp;
}

六、重要字段解析

name(緩存名稱(chēng))

表示 slab 緩存的名稱(chēng),存儲(chǔ)相同類(lèi)型和大小的對(duì)象。例如 /proc/slabinfo 文件中的kmem_cache字段表示緩存的對(duì)象名,如task_struct。每個(gè) slab 緩存存儲(chǔ)相同類(lèi)型和大小的對(duì)象,例如kmalloc-32是用于分配 32 字節(jié)的內(nèi)存塊。

active_objs(活動(dòng)對(duì)象數(shù))

當(dāng)前在使用的對(duì)象數(shù)量。該字段表示當(dāng)前在使用的(已分配的)對(duì)象數(shù)量,即系統(tǒng)中實(shí)際分配給內(nèi)核使用的對(duì)象數(shù)量。例如,在系統(tǒng)中實(shí)際分配給內(nèi)核使用的進(jìn)程控制塊等對(duì)象的數(shù)量就是活動(dòng)對(duì)象數(shù)。

num_objs(總對(duì)象數(shù))

緩存中分配的對(duì)象總數(shù)。這個(gè)字段表示緩存中分配的對(duì)象總數(shù),包括已經(jīng)分配和空閑的對(duì)象。這個(gè)值通常大于或等于active_objs。比如一個(gè)特定大小的 slab 緩存中,所有已經(jīng)分配出去和尚未分配但已準(zhǔn)備好的對(duì)象總數(shù)就是num_objs。

objsize(對(duì)象大?。?/h3>

每個(gè)對(duì)象的大小。該字段表示每個(gè)對(duì)象的大小(以字節(jié)為單位),即 slab 緩存中每個(gè)對(duì)象占用的內(nèi)存空間大小。例如對(duì)于特定的內(nèi)核數(shù)據(jù)結(jié)構(gòu),其對(duì)象大小可以通過(guò)這個(gè)字段確定。

objperslab(每個(gè) slab 包含的對(duì)象數(shù))

每個(gè) slab 中包含的對(duì)象數(shù)量。每個(gè) slab 是一個(gè)較大的內(nèi)存塊,其中包含多個(gè)對(duì)象,這個(gè)字段表示每個(gè) slab 中具體包含的對(duì)象數(shù)量。例如對(duì)于一個(gè)特定大小的 slab,根據(jù)對(duì)象大小和 slab 總大小,可以計(jì)算出objperslab的值。

pagesperslab(每個(gè) slab 使用的頁(yè)數(shù))

每個(gè) slab 使用的頁(yè)數(shù),通常為 4KB 大小。該字段表示每個(gè) slab 使用的頁(yè)數(shù)。Linux 內(nèi)核使用分頁(yè)機(jī)制來(lái)管理內(nèi)存,頁(yè)面通常為 4KB 大小。一個(gè) slab 由一定數(shù)量的連續(xù)物理頁(yè)組成,這個(gè)字段反映了每個(gè) slab 占用的物理頁(yè)數(shù)量。

tunables(可調(diào)參數(shù))

控制 slab 緩存的行為,如limit、batchcount、sharedfactor等。這個(gè)字段中的值控制 slab 緩存的行為:

  • limit:slab 緩存中每個(gè) CPU 可以緩存的最大對(duì)象數(shù)量。
  • batchcount:每次從全局緩存到 CPU 本地緩存中批量獲取的對(duì)象數(shù)。
  • sharedfactor:控制多個(gè) CPU 是否共享 slab 緩存。

slabdata(slab 統(tǒng)計(jì)信息)

包含有關(guān) slab 使用的統(tǒng)計(jì)數(shù)據(jù),如active_slabs、num_slabs、sharedavail等。這部分包含有關(guān) slab 使用的統(tǒng)計(jì)數(shù)據(jù):

  • active_slabs:當(dāng)前正在使用的 slab 數(shù)量。
  • num_slabs:系統(tǒng)中分配的總 slab 數(shù)量。
  • sharedavail:CPU 本地緩存中可用對(duì)象的數(shù)量。通過(guò)這些統(tǒng)計(jì)數(shù)據(jù),管理員可以了解系統(tǒng)中 slab 分配器的內(nèi)存使用情況,幫助調(diào)優(yōu)系統(tǒng)性能。

七、高速緩存分類(lèi)

7.1 普通高速緩存

普通高速緩存為 kmem_cache結(jié)構(gòu)本身和內(nèi)核提供通用高速緩存。它通過(guò)通用高速緩存實(shí)現(xiàn)小塊連續(xù)內(nèi)存的分配,為內(nèi)核提供了一種高效的內(nèi)存管理方式。

普通高速緩存首先會(huì)為 kmem_cache結(jié)構(gòu)本身提供高速緩存,這類(lèi)緩存保存在 cache_cache變量中。cache_cache變量代表著 cache_chain 鏈表中的第一個(gè)元素,它保存著對(duì)高速緩存描述符的高速緩存。

通用高速緩存所提供的對(duì)象具有幾何分布的大小,范圍為 32 到 131072 字節(jié)。內(nèi)核中提供了 kmalloc()和 kfree()兩個(gè)接口分別進(jìn)行內(nèi)存的申請(qǐng)和釋放。

7.2 專(zhuān)用高速緩存

專(zhuān)用高速緩存是根據(jù)內(nèi)核所需,通過(guò)指定具體的對(duì)象而創(chuàng)建。它提供一套完整的接口用于高速緩存的申請(qǐng)、釋放以及 slab 的申請(qǐng)和釋放。

內(nèi)核為專(zhuān)用高速緩存的申請(qǐng)和釋放提供了接口,kmem_cache_create()用于對(duì)一個(gè)指定的對(duì)象創(chuàng)建高速緩存。它從 cache_cache普通高速緩存中為新的專(zhuān)有緩存分配一個(gè)高速緩存描述符,并把這個(gè)描述符插入到高速緩存描述符形成的 cache_chain鏈表中。kmem_cache_destory()用于撤銷(xiāo)一個(gè)高速緩存,并將它從 cache_chain鏈表上刪除。

對(duì)于 slab 的申請(qǐng)和釋放,kmem_cache_alloc()在其參數(shù)所指定的高速緩存中分配一個(gè) slab。相反,kmem_cache_free()在其參數(shù)所指定的高速緩存中釋放一個(gè) slab。

八、應(yīng)用案例分析

8.1 定義和使用特定大小的對(duì)象

以下以專(zhuān)用 slab 緩存為例,展示如何定義和使用特定大小的對(duì)象。

首先,我們需要為特定的結(jié)構(gòu)體創(chuàng)建一個(gè)專(zhuān)用的 slab 緩存。假設(shè)我們有一個(gè)名為sample_struct的結(jié)構(gòu)體:

struct sample_struct {
    int id;
    char name[20];
    char address[50];
};

我們可以使用kmem_cache_create函數(shù)來(lái)創(chuàng)建一個(gè)用于存儲(chǔ)sample_struct結(jié)構(gòu)體的專(zhuān)用 slab 緩存:

static struct kmem_cache *sample_struct_cachep;
static void init_sample_struct_cache( void ){
    sample_struct_cachep = kmem_cache_create(
        "sample_struct_cachep",  /* Name */
        sizeof(struct sample_struct), /* Object Size */
        0,     /* Alignment */
        SLAB_HWCACHE_ALIGN,  /* Flags */
        NULL);  /* Constructor */
    return;
}

這里創(chuàng)建的特定緩存包含sample_struct大小的對(duì)象,并且是硬件緩存對(duì)齊的(由標(biāo)志參數(shù)SLAB_HWCACHE_ALIGN定義)。

使用所分配的 slab 緩存對(duì)象可以通過(guò)以下方式進(jìn)行:

int slab_test( void ){
    struct sample_struct *object;
    printk( "Cache name is %s\n", kmem_cache_name( sample_struct_cachep ) );
    printk( "Cache object size is %d\n", kmem_cache_size( sample_struct_cachep ) );
    object = kmem_cache_alloc(sample_struct_cachep, GFP_KERNEL);
    if (object) {
        // 使用 object...
        kmem_cache_free(sample_struct_cachep, object);
    }
    return 0;
}

8.2 銷(xiāo)毀緩存

調(diào)用者必須確保在執(zhí)行銷(xiāo)毀操作過(guò)程中,不要從緩存中分配對(duì)象。銷(xiāo)毀緩存的主要步驟如下:

  • 將緩存從cache_chain鏈表中刪除。
  • 將本地高速緩存、alien 高速緩存和共享本地高速緩存中的對(duì)象都釋放回 slab,并釋放所有的 free 鏈表,然后判斷 full 鏈表以及 partial 鏈表是否都為空,如果有一個(gè)不為空說(shuō)明存在非空閑 slab,也就是說(shuō)有對(duì)象還未釋放,此時(shí)無(wú)法銷(xiāo)毀緩存,重新將緩存添加到cache_chain鏈表中。
  • 確定所有的 slab 都為空閑狀態(tài)后,將緩存涉及到的所有描述符都釋放(這些描述符都是保存在普通高速緩存中的)。

負(fù)責(zé)銷(xiāo)毀緩存的函數(shù)為kmem_cache_destroy,其代碼實(shí)現(xiàn)如下:

void kmem_cache_destroy(struct kmem_cache *cachep){
    BUG_ON(!cachep || in_interrupt());
    /* Find the cache in the chain of caches. */
    get_online_cpus();
    mutex_lock(&cache_chain_mutex);
    /** the chain is never empty, cache_cache is never destroyed*//*將 cache 從 cache_chain 中刪除*/
    list_del(&cachep->next);
    /*釋放完 free 鏈表,如果 FULL 鏈表或 partial 鏈表中還有 slab,說(shuō)明還有對(duì)象處于分配狀態(tài)因此不能銷(xiāo)毀該緩存!*/
    if (__cache_shrink(cachep)) {
        slab_error(cachep, "Can't free all objects");
        /*重新將緩存添加到 cache_chain 鏈表中*/
        list_add(&cachep->next, &cache_chain);
        mutex_unlock(&cache_chain_mutex);
        put_online_cpus();
        return;
    }
    if (unlikely(cachep->flags & SLAB_DESTROY_BY_RCU))
        rcu_barrier();
    /*釋放 cache 所涉及到的各個(gè)描述符的存儲(chǔ)對(duì)象*/
    __kmem_cache_destroy(cachep);
    mutex_unlock(&cache_chain_mutex);
    put_online_cpus();
}

static int __cache_shrink(struct kmem_cache *cachep){
    int ret = 0, i = 0;
    struct kmem_list3 *l3;
    /*將本地高速緩存,share 本地高速緩存以及 alien 高速緩存的空閑對(duì)象釋放 slab*/
    drain_cpu_caches(cachep);
    check_irq_on();
    for_each_online_node(i) {
        l3 = cachep->nodelists[i];
        if (!l3) continue;
        /*銷(xiāo)毀空閑鏈表中的 slab*/
        drain_freelist(cachep, l3, l3->free_objects);
        /*判斷 full 和 partial 是否為空,有一個(gè)不為空則 ret 就為 1*/
        ret +=!list_empty(&l3->slabs_full) ||!list_empty(&l3->slabs_partial);
    }
    return (ret? 1 : 0);
}

drain_cpu_caches()的最終落腳在 free_block()函數(shù)上,該函數(shù)在前面已做過(guò)分析,在此不再列出。

static int drain_freelist(struct kmem_cache *cache, struct kmem_list3 *l3, int tofree){
    struct list_head *p;
    int nr_freed;
    struct slab *slabp;
    nr_freed = 0;
    /*slab 中的對(duì)象還未釋放完并且 free 鏈表不為空*/
    while (nr_freed < tofree &&!list_empty(&l3->slabs_free)) {
        spin_lock_irq(&l3->list_lock);
        p = l3->slabs_free.prev;
        if (p == &l3->slabs_free) {/*鏈表中已無(wú)元素*/
            spin_unlock_irq(&l3->list_lock);
            goto out;
        }
        /*從 free 鏈表中取出一個(gè) slab*/
        slabp = list_entry(p, struct slab, list);
#if DEBUG
        BUG_ON(slabp->inuse);
#endif
        /*從鏈表中刪除*/
        list_del(&slabp->list);
        /** Safe to drop the lock. The slab is no longer linked* to the cache.*//*空閑對(duì)象數(shù)量總數(shù)減去 num*/
        l3->free_objects -= cache->num;
        spin_unlock_irq(&l3->list_lock);
        /*銷(xiāo)毀 slab*/
        slab_destroy(cache, slabp);
        nr_freed++;
    }
out:
    return nr_freed;
}

slab_destroy()函數(shù)已在前文中分析。

static void __kmem_cache_destroy(struct kmem_cache *cachep){
    int i;
    struct kmem_list3 *l3;
    /*釋放存儲(chǔ)本地高速緩存描述符的對(duì)象*/
    for_each_online_cpu(i)
        kfree(cachep->array[i]);
    /* NUMA: free the list3 structures */
    for_each_online_node(i) {
        l3 = cachep->nodelists[i];
        if (l3) {
            /*釋放存儲(chǔ)共享本地高速緩存描述符的對(duì)象*/
            kfree(l3->shared);
            /*釋放存儲(chǔ) alien 本地高速緩存描述符的對(duì)象*/
            free_alien_cache(l3->alien);
            /*釋放存儲(chǔ) kmem_list3 描述符的對(duì)象*/
            kfree(l3);
        }
        /*釋放存儲(chǔ)緩存描述符的對(duì)象*/
        kmem_cache_free(&cache_cache, cachep);
    }}


責(zé)任編輯:武曉燕 來(lái)源: 深度Linux
相關(guān)推薦

2009-12-25 15:34:54

slab分配器

2021-08-03 09:02:58

LinuxSlab算法

2025-04-11 00:44:00

2013-10-12 11:15:09

Linux運(yùn)維內(nèi)存管理

2017-02-08 08:40:21

C++固定內(nèi)存塊

2017-01-17 16:17:48

C++固定分配器

2017-01-20 14:21:35

內(nèi)存分配器存儲(chǔ)

2024-10-11 10:00:20

2023-04-03 08:25:02

Linux內(nèi)存slub

2025-05-27 02:45:45

2020-12-15 08:54:06

Linux內(nèi)存碎片化

2020-03-11 13:44:20

編程語(yǔ)言PythonJava

2013-10-14 10:41:41

分配器buddy syste

2025-02-10 07:30:00

malloc內(nèi)存分配器內(nèi)存

2023-12-22 07:55:38

Go語(yǔ)言分配策略

2014-09-01 10:09:44

Linux

2013-10-11 17:24:47

Linux運(yùn)維內(nèi)存管理

2022-02-23 16:49:19

Linux內(nèi)存數(shù)據(jù)結(jié)構(gòu)

2018-12-06 10:22:54

Linux內(nèi)核內(nèi)存

2023-04-13 14:42:26

PoE供電器PoE交換機(jī)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)