嵌入式系統(tǒng)e_slab的研究與實(shí)現(xiàn)
隨著硬件技術(shù)的發(fā)展和內(nèi)存容量的擴(kuò)大,操作系統(tǒng)中內(nèi)存管理技術(shù)日趨完善。但是在嵌入式領(lǐng)域中,硬件性能和內(nèi)存容量遠(yuǎn)遠(yuǎn)落后于PC機(jī),其內(nèi)存管理受到多種因素制約,若直接采用操作系統(tǒng)中的內(nèi)存管理技術(shù),不僅難以達(dá)到預(yù)期效果,而且會(huì)影響嵌入式系統(tǒng)的性能。
在嵌入式系統(tǒng)內(nèi)存管理設(shè)計(jì)過(guò)程中,發(fā)現(xiàn)操作系統(tǒng)中的slab分配器雖然在PC機(jī)上有良好的性能,但是在嵌入式系統(tǒng)中不但不能發(fā)揮其優(yōu)勢(shì),還降低了系統(tǒng)的整體性能。本文通過(guò)分析,指出了slab分配器的不足,并給出相應(yīng)的解決方案。實(shí)驗(yàn)結(jié)果表明,slab分配器經(jīng)過(guò)改進(jìn)可適用于嵌入式系統(tǒng)。
1 slab分配器分析
操作系統(tǒng)內(nèi)核運(yùn)行時(shí)會(huì)頻繁地為某些對(duì)象分配內(nèi)存空間,而這些對(duì)象往往只需要幾十或幾百KB的空間,如果直接采用頁(yè)面管理器進(jìn)行內(nèi)存分配,將產(chǎn)生很多內(nèi)存碎片,造成嚴(yán)重的內(nèi)存浪費(fèi)。slab分配器支持細(xì)粒度的內(nèi)存分配,較好地解決了此問(wèn)題。由于性能優(yōu)越,slab被Linux、FreeBSD等操作系統(tǒng)采用,是目前應(yīng)用最廣的內(nèi)核內(nèi)存管理器之一[1]。
1.1 slab分配器設(shè)計(jì)思想
基于頁(yè)面分配器[2],將一頁(yè)或幾頁(yè)的內(nèi)存組織起來(lái),劃分成一定數(shù)量的小塊內(nèi)存,這種連續(xù)的頁(yè)面稱之為slab。它為內(nèi)核中使用頻繁的對(duì)象建立專門(mén)的緩沖區(qū)(cache),每種類(lèi)型的對(duì)象都有自己專用的cache[2]。一個(gè)cache管理著多個(gè)slab,每個(gè)slab又管理著多個(gè)對(duì)象。slab的大小與所管理對(duì)象的大小有關(guān)。根據(jù)slab管理對(duì)象的分配情況,可將每個(gè)cache中的slab分為3類(lèi)[3-4]:(1)slab管理的對(duì)象已經(jīng)完全分配,沒(méi)有空閑的對(duì)象;(2)slab管理的對(duì)象部分分配,還有部分空閑對(duì)象;(3)slab中的對(duì)象都未分配,都是空閑對(duì)象。
不同的slab分別放入不同的隊(duì)列中,即每個(gè)cache管理3個(gè)slab隊(duì)列,cache與cache之間的關(guān)系如圖1虛框①內(nèi)所示,cache與slab的關(guān)系如圖1虛框②內(nèi)所示。
當(dāng)slab分配器接收到內(nèi)存申請(qǐng)時(shí),根據(jù)所申請(qǐng)內(nèi)存的大小找到合適的cache,從cache管理的第二類(lèi)slab中分配對(duì)象,若失敗則從第三類(lèi)slab中分配對(duì)象,若還不成功則說(shuō)明cache中沒(méi)有空閑對(duì)象,須為cache創(chuàng)建一個(gè)新的slab,從新的slab中分配空閑對(duì)象。
對(duì)象釋放過(guò)程中,不僅要清空對(duì)象占用的空間,而且還要調(diào)整對(duì)象所屬slab的狀態(tài),判斷是否改變此slab在cache中的位置。
slab分配器采用著色機(jī)制將不同slab中的對(duì)象放入不同的偏移處,利用硬件高速緩存的映射機(jī)制,將頁(yè)的不同偏移映射到硬件緩存的不同地址。而每個(gè)slab的開(kāi)始部分訪問(wèn)頻率最高,只要slab中起始對(duì)象的偏移不同則映射到硬件高速緩存的位置就不同,從而降低了頻繁換入換出的性能損失[4-5]。
1.2 slab分配器在嵌入式系統(tǒng)中的缺陷
slab分配器雖然能解決系統(tǒng)對(duì)小塊內(nèi)存的頻繁需求,但是管理結(jié)構(gòu)復(fù)雜,內(nèi)存分配策略開(kāi)銷(xiāo)較大。在內(nèi)存受限的嵌入式系統(tǒng)中,slab的缺陷大大影響了系統(tǒng)的整體性能??傊?,slab分配器存在以下三方面的缺陷:
(1)slab管理結(jié)構(gòu)和存儲(chǔ)開(kāi)銷(xiāo)較大
每個(gè)slab由slab描述結(jié)構(gòu)、管理空閑對(duì)象的整型數(shù)組和對(duì)象三部分組成,整型數(shù)組把slab中空閑對(duì)象組成一個(gè)順序隊(duì)列,數(shù)組大小與對(duì)象數(shù)有關(guān),每個(gè)對(duì)象對(duì)應(yīng)一個(gè)整數(shù),如圖2所示。當(dāng)對(duì)象較小時(shí),整型數(shù)組將造成較大的內(nèi)存開(kāi)銷(xiāo)。
(2)cache結(jié)構(gòu)復(fù)雜而且數(shù)量較多
系統(tǒng)中存在著專用對(duì)象和通用對(duì)象。專用對(duì)象專門(mén)存儲(chǔ)特定用途的數(shù)據(jù)結(jié)構(gòu),例如CPU、文件系統(tǒng)等,其數(shù)量與系統(tǒng)密切相關(guān);通用對(duì)象用來(lái)存儲(chǔ)一般的數(shù)據(jù)結(jié)構(gòu),大小在幾十KB到幾千KB之間(一般為2的整次冪字節(jié)),有十多種。不管是專用對(duì)象還是通用對(duì)象,slab分配器都為其建立了一個(gè)cache結(jié)構(gòu),眾多cache組織和管理的較大開(kāi)銷(xiāo)是嵌入式系統(tǒng)難以承受的。
(3)復(fù)雜的隊(duì)列管理
如圖1所示,slab分配器中存在較多的隊(duì)列,每個(gè)cache管理著3個(gè)slab隊(duì)列,每個(gè)slab隊(duì)列與cache組成循環(huán)隊(duì)列。所有的cache組成雙向循環(huán)隊(duì)列。面對(duì)眾多的隊(duì)列,如何有效地管理是很困難的。
1.3 slab在嵌入式系統(tǒng)中的改進(jìn)
針對(duì)上節(jié)中slab分配器的三點(diǎn)缺陷,給出相應(yīng)的改進(jìn)方案。
(1)改進(jìn)slab結(jié)構(gòu)
針對(duì)slab中對(duì)象管理數(shù)組開(kāi)銷(xiāo)過(guò)大的問(wèn)題,可以將多個(gè)不同的slab合并成一個(gè)slab,從而減少slab的數(shù)量,即一個(gè)slab管理對(duì)象的大小可在一個(gè)小范圍內(nèi)浮動(dòng)。由于slab中對(duì)象大小不同,無(wú)法確定slab中對(duì)象的大小、數(shù)量和位置,所以必須重新設(shè)置slab結(jié)構(gòu)。
(2)限制slab分配器管理的內(nèi)存粒度范圍
由于內(nèi)核內(nèi)存管理器主要負(fù)責(zé)細(xì)粒度的內(nèi)存管理,所以限制所管理對(duì)象的大小。對(duì)于大塊內(nèi)存的申請(qǐng),直接由頁(yè)面分配器處理。
(3)精簡(jiǎn)隊(duì)列管理
簡(jiǎn)化cache中繁雜的隊(duì)列,將cache中的前兩個(gè)slab隊(duì)列合并成一個(gè)隊(duì)列。
本文將經(jīng)過(guò)上述三方面改進(jìn)的分配器稱之為e_slab分配器。
2 e_slab分配器設(shè)計(jì)
2.1 基本管理結(jié)構(gòu)
e_slab分配器有3個(gè)重要的基本結(jié)構(gòu),下面分別對(duì)其作相關(guān)介紹。
(1)object_t結(jié)構(gòu)
typedef struct object {
unsigned long size;
unsigned long offset;
} object_t;
object_t是描述對(duì)象的基本結(jié)構(gòu),每個(gè)對(duì)象對(duì)應(yīng)一個(gè)object_t結(jié)構(gòu),它描述了對(duì)象的大小和下一個(gè)空閑對(duì)象的地址。
(2)e_slab_t結(jié)構(gòu)
typedef struct e_slab _s {
struct list_head list;
void *s_mem;
unsigned int units;
unsigned int free;
} e_slab _t;
e_slab _t是管理對(duì)象的基本結(jié)構(gòu),它不僅描述了本結(jié)構(gòu)的頁(yè)塊起始地址,而且存儲(chǔ)了空閑對(duì)象的數(shù)量和地址等信息。
object_t、e_slab _t和對(duì)象結(jié)構(gòu)如圖3虛框②內(nèi)所示。
(3)cache結(jié)構(gòu)
typedef struct cache_s {
struct list_head next;
struct list_head slab_list;[!--empirenews.page--]
unsigned int objsize;
unsigned int gfporder;
unsigned int num;
…
} cache_t;
cache的描述結(jié)構(gòu)為cache_t,它主要描述了所管e_slab的基本信息。由于cache_t結(jié)構(gòu)大小相同,可把cache_t看做一個(gè)專用對(duì)象,所有的cache組織在一起。
cache管理的所有e_slab被加入到list隊(duì)列。把管理所有cache的結(jié)構(gòu)稱之為cache_cache。cache_cache與cache之間有兩種關(guān)系:一種是雙向隊(duì)列關(guān)系,如圖3虛框①內(nèi)所示,cache_cache利用雙向鏈表將系統(tǒng)中所有的cache(包括專用cache和通用cache)組成循環(huán)隊(duì)列;一種是cache與對(duì)象之間的關(guān)系。
2.2 e_slab分配器初始化
e_slab分配器初始化主要完成cache、e_slab_t等結(jié)構(gòu)的創(chuàng)建,為對(duì)象的分配做好準(zhǔn)備。
2.2.1 cache的創(chuàng)建
cache_cache是系統(tǒng)中所有cache的管理者,它的創(chuàng)建優(yōu)先于所有的cache。系統(tǒng)會(huì)為每種對(duì)象創(chuàng)建一個(gè)cache,創(chuàng)建流程如下:
(1)申請(qǐng)一個(gè)cache空間。從cache_cache中分配一個(gè)專用對(duì)象,即cache。設(shè)置cache中的各個(gè)域,包括管理的對(duì)象大小的上限和e_slab大小,其中e_slab大小與對(duì)象大小有關(guān)。
(2)將此cache加入管理隊(duì)列。將cache加入cache_cache組成的雙向隊(duì)列中,雙向隊(duì)列采用通用鏈表鏈接所有的cache。
(3)將cache加入分配隊(duì)列。用一個(gè)全局的cache指針指向生成的cache。
2.2.2 e_slab的創(chuàng)建
在cache的創(chuàng)建過(guò)程中,需為每個(gè)cache創(chuàng)建一個(gè)e_slab。e_slab中的對(duì)象全部空閑,可供分配,其流程如下:
(1)借助頁(yè)面分配器申請(qǐng)連續(xù)物理頁(yè)面。根據(jù)e_slab申請(qǐng)的大小和是否有DMA請(qǐng)求,在相應(yīng)的內(nèi)存區(qū)申請(qǐng)連續(xù)頁(yè)塊。
(2)設(shè)置頁(yè)面屬性。主要設(shè)置該頁(yè)面的e_slab標(biāo)志,并將該頁(yè)塊與cache和e_slab關(guān)聯(lián)。
(3)設(shè)置e_slab描述結(jié)構(gòu),初始化對(duì)象結(jié)構(gòu)。
2.3 對(duì)象的分配與釋放
對(duì)象的分配與釋放是內(nèi)存管理模塊提供的兩個(gè)基本接口。
2.3.1 對(duì)象的分配
當(dāng)系統(tǒng)需要小內(nèi)存塊或者專用對(duì)象時(shí),系統(tǒng)會(huì)調(diào)用對(duì)象分配操作,完成對(duì)對(duì)象的分配,具體流程如圖4所示。
(1)找到對(duì)應(yīng)的cache。根據(jù)申請(qǐng)對(duì)象的大小定位相應(yīng)的cache。
(2)確定對(duì)應(yīng)的e_slab。檢查cache中的e_slab,找到滿足本次請(qǐng)求的e_slab,如果所有的e_slab均不能滿足,則創(chuàng)建一個(gè)新的e_slab并添加到cache管理的隊(duì)列中。
(3)從e_slab中分配一個(gè)空閑對(duì)象。從e_slab為系統(tǒng)分配一個(gè)空閑對(duì)象和object_t結(jié)構(gòu),將對(duì)象返還給系統(tǒng),調(diào)整e_slab中對(duì)象,管理數(shù)組結(jié)構(gòu)。
2.3.2 對(duì)象的釋放
系統(tǒng)使用完對(duì)象后,應(yīng)及時(shí)釋放對(duì)象,否則內(nèi)存會(huì)越用越少。對(duì)象釋放流程如圖5所示。
(1)確定對(duì)象對(duì)應(yīng)的cache與e_slab。根據(jù)對(duì)象的地址可以獲得所在頁(yè)面的描述符結(jié)構(gòu),從而獲得對(duì)應(yīng)的cache和e_slab。
(2)釋放對(duì)象。獲得對(duì)象在e_slab中的偏移,采用頭插法將對(duì)象加入空閑對(duì)象隊(duì)列,并使e_slab中空閑內(nèi)存增加釋放值。
(3)e_slab的調(diào)整。檢查e_slab中空閑內(nèi)存大小,若等于e_slab中所有對(duì)象都釋放,則清除頁(yè)面的e_slab標(biāo)志,并把e_slab占用頁(yè)塊歸還給物理內(nèi)存管理器。
2.4 e_slab分配器的回收
在系統(tǒng)退出、內(nèi)存回收等不再需要e_slab分配器時(shí),需進(jìn)行e_slab分配器的回收,主要完成e_slab的釋放和cache的釋放。
2.4.1 e_slab的釋放
在對(duì)象釋放過(guò)程中,若發(fā)現(xiàn)某個(gè)e_slab已經(jīng)全部空閑,沒(méi)有分配的對(duì)象,則將其釋放,流程如下:
(1)將e_slab從cache結(jié)構(gòu)中刪除。e_slab從cache的list隊(duì)列中摘掉。
(2)清除頁(yè)面標(biāo)志。將e_slab所在物理頁(yè)面的e_slab標(biāo)志清除,并清除頁(yè)面與e_slab和cache的關(guān)聯(lián),使頁(yè)面回到初始狀態(tài)。
2.4.2 cache的釋放
當(dāng)系統(tǒng)不再使用某種對(duì)象時(shí),系統(tǒng)要銷(xiāo)毀管理對(duì)象的cache。cache銷(xiāo)毀流程如下:
(1)將cache從管理隊(duì)列摘掉。將cache從cache_cache組成的雙向隊(duì)列中刪除。
(2)確定cache中沒(méi)有e_slab。在cache銷(xiāo)毀前,必須確定所管理的對(duì)象都已釋放,檢查cache的list隊(duì)列,為空則cache中沒(méi)有e_slab,否則進(jìn)行e_slab釋放。
(3)釋放cache結(jié)構(gòu)占用的內(nèi)存。由于cache是cache_cache管理的對(duì)象,cache結(jié)構(gòu)的釋放過(guò)程就是對(duì)象的釋放過(guò)程。
3 性能測(cè)試
在嵌入式系統(tǒng)內(nèi)存管理設(shè)計(jì)過(guò)程中,分別采用頁(yè)面分配器與slab分配器相結(jié)合的方案和頁(yè)面分配器與e_slab分配器相結(jié)合的方案,比較兩種方案中slab和e_slab管理結(jié)構(gòu)的內(nèi)存占用量和內(nèi)存分配釋放中的性能。
在管理結(jié)構(gòu)內(nèi)存占用方面,e_slab比slab節(jié)省了43%的空間;在對(duì)象的c過(guò)程中,e_slab的速度比slab快8%;內(nèi)存釋放過(guò)程中,e_slab比slab快5%。可見(jiàn),不管在時(shí)間上還是空間上,e_slab性能都比slab優(yōu)越。