Slab
所謂尺有所長,寸有所短。以頁為最小單位分配內(nèi)存對于內(nèi)核管理系統(tǒng)中的物理內(nèi)存來說的確比較方便,但內(nèi)核自身最常使用的內(nèi)存卻往往是很?。ㄟh遠小于一頁)的內(nèi)存塊——比如存放文件描述符、進程描述符、虛擬內(nèi)存區(qū)域描述符等行為所需的內(nèi)存都不足一頁。這些用來存放描述符的內(nèi)存相比頁面而言,就好比是面包屑與面包。一個整頁中可以聚集多個這些小塊內(nèi)存;而且這些小塊內(nèi)存塊也和面包屑一樣頻繁地生成/銷毀。
為了滿足內(nèi)核對這種小內(nèi)存塊的需要,Linux系統(tǒng)采用了一種被稱為slab分配器的技術(shù)。Slab分配器的實現(xiàn)相當(dāng)復(fù)雜,但原理不難,其核心思想就是“存儲池[4]”的運用。內(nèi)存片段(小塊內(nèi)存)被看作對象,當(dāng)被使用完后,并不直接釋放而是被緩存到“存儲池”里,留做下次使用,這無疑避免了頻繁創(chuàng)建與銷毀對象所帶來的額外負載。
Slab技術(shù)不但避免了內(nèi)存內(nèi)部分片(下文將解釋)帶來的不便(引入Slab分配器的主要目的是為了減少對伙伴系統(tǒng)分配算法的調(diào)用次數(shù)——頻繁分配和回收必然會導(dǎo)致內(nèi)存碎片——難以找到大塊連續(xù)的可用內(nèi)存),而且可以很好地利用硬件緩存提高訪問速度。
Slab并非是脫離伙伴關(guān)系而獨立存在的一種內(nèi)存分配方式,slab仍然是建立在頁面基礎(chǔ)之上,換句話說,Slab將頁面(來自于伙伴關(guān)系管理的空閑頁面鏈表)撕碎成眾多小內(nèi)存塊以供分配,slab中的對象分配和銷毀使用kmem_cache_alloc與kmem_cache_free。
Kmalloc
Slab分配器不僅僅只用來存放內(nèi)核專用的結(jié)構(gòu)體,它還被用來處理內(nèi)核對小塊內(nèi)存的請求。當(dāng)然鑒于Slab分配器的特點,一般來說內(nèi)核程序中對小于一頁的小塊內(nèi)存的請求才通過Slab分配器提供的接口Kmalloc來完成(雖然它可分配32 到131072字節(jié)的內(nèi)存)。從內(nèi)核內(nèi)存分配的角度來講,kmalloc可被看成是get_free_page(s)的一個有效補充,內(nèi)存分配粒度更靈活了。
有興趣的話,可以到/proc/slabinfo中找到內(nèi)核執(zhí)行現(xiàn)場使用的各種slab信息統(tǒng)計,其中你會看到系統(tǒng)中所有slab的使用信息。從信息中可以看到系統(tǒng)中除了專用結(jié)構(gòu)體使用的slab外,還存在大量為Kmalloc而準(zhǔn)備的Slab(其中有些為dma準(zhǔn)備的)。
內(nèi)核非連續(xù)內(nèi)存分配(Vmalloc)
伙伴關(guān)系也好、slab技術(shù)也好,從內(nèi)存管理理論角度而言目的基本是一致的,它們都是為了防止“分片”,不過分片又分為外部分片和內(nèi)部分片之說,所謂內(nèi)部分片是說系統(tǒng)為了滿足一小段內(nèi)存區(qū)(連續(xù))的需要,不得不分配了一大區(qū)域連續(xù)內(nèi)存給它,從而造成了空間浪費;外部分片是指系統(tǒng)雖有足夠的內(nèi)存,但卻是分散的碎片,無法滿足對大塊“連續(xù)內(nèi)存”的需求。無論何種分片都是系統(tǒng)有效利用內(nèi)存的障礙。slab分配器使得一個頁面內(nèi)包含的眾多小塊內(nèi)存可獨立被分配使用,避免了內(nèi)部分片,節(jié)約了空閑內(nèi)存?;锇殛P(guān)系把內(nèi)存塊按大小分組管理,一定程度上減輕了外部分片的危害,因為頁框分配不在盲目,而是按照大小依次有序進行,不過伙伴關(guān)系只是減輕了外部分片,但并未徹底消除。你自己比劃一下多次分配頁面后,空閑內(nèi)存的剩余情況吧。
所以避免外部分片的最終思路還是落到了如何利用不連續(xù)的內(nèi)存塊組合成“看起來很大的內(nèi)存塊”——這里的情況很類似于用戶空間分配虛擬內(nèi)存,內(nèi)存邏輯上連續(xù),其實映射到并不一定連續(xù)的物理內(nèi)存上。Linux內(nèi)核借用了這個技術(shù),允許內(nèi)核程序在內(nèi)核地址空間中分配虛擬地址,同樣也利用頁表(內(nèi)核頁表)將虛擬地址映射到分散的內(nèi)存頁上。以此完美地解決了內(nèi)核內(nèi)存使用中的外部分片問題。內(nèi)核提供vmalloc函數(shù)分配內(nèi)核虛擬內(nèi)存,該函數(shù)不同于kmalloc,它可以分配較Kmalloc大得多的內(nèi)存空間(可遠大于128K,但必須是頁大小的倍數(shù)),但相比Kmalloc來說,Vmalloc需要對內(nèi)核虛擬地址進行重映射,必須更新內(nèi)核頁表,因此分配效率上要低一些(用空間換時間)
與用戶進程相似,內(nèi)核也有一個名為init_mm的mm_strcut結(jié)構(gòu)來描述內(nèi)核地址空間,其中頁表項pdg=swapper_pg_dir包含了系統(tǒng)內(nèi)核空間(3G-4G)的映射關(guān)系。因此vmalloc分配內(nèi)核虛擬地址必須更新內(nèi)核頁表,而kmalloc或get_free_page由于分配的連續(xù)內(nèi)存,所以不需要更新內(nèi)核頁表。
vmalloc分配的內(nèi)核虛擬內(nèi)存與kmalloc/get_free_page分配的內(nèi)核虛擬內(nèi)存位于不同的區(qū)間,不會重疊。因為內(nèi)核虛擬空間被分區(qū)管理,各司其職。進程空間地址分布從0到3G(其實是到PAGE_OFFSET, 在0x86中它等于0xC0000000),從3G到vmalloc_start這段地址是物理內(nèi)存映射區(qū)域(該區(qū)域中包含了內(nèi)核鏡像、物理頁面表mem_map等等)比如我使用的系統(tǒng)內(nèi)存是64M(可以用free看到),那么(3G——3G+64M)這片內(nèi)存就應(yīng)該映射到物理內(nèi)存,而vmalloc_start位置應(yīng)在3G+64M附近(說"附近"因為是在物理內(nèi)存映射區(qū)與vmalloc_start期間還會存在一個8M大小的gap來防止躍界),vmalloc_end的位置接近4G(說"接近"是因為最后位置系統(tǒng)會保留一片128k大小的區(qū)域用于專用頁面映射,還有可能會有高端內(nèi)存映射區(qū),這些都是細節(jié),這里我們不做糾纏)。
上圖是內(nèi)存分布的模糊輪廓
由get_free_page或Kmalloc函數(shù)所分配的連續(xù)內(nèi)存都陷于物理映射區(qū)域,所以它們返回的內(nèi)核虛擬地址和實際物理地址僅僅是相差一個偏移量(PAGE_OFFSET),你可以很方便的將其轉(zhuǎn)化為物理內(nèi)存地址,同時內(nèi)核也提供了virt_to_phys()函數(shù)將內(nèi)核虛擬空間中的物理映射區(qū)地址轉(zhuǎn)化為物理地址。要知道,物理內(nèi)存映射區(qū)中的地址與內(nèi)核頁表是有序?qū)?yīng)的,系統(tǒng)中的每個物理頁面都可以找到它對應(yīng)的內(nèi)核虛擬地址(在物理內(nèi)存映射區(qū)中的)。
而vmalloc分配的地址則限于vmalloc_start與vmalloc_end之間。每一塊vmalloc分配的內(nèi)核虛擬內(nèi)存都對應(yīng)一個vm_struct結(jié)構(gòu)體(可別和vm_area_struct搞混,那可是進程虛擬內(nèi)存區(qū)域的結(jié)構(gòu)),不同的內(nèi)核虛擬地址被4k大小的空閑區(qū)間隔,以防止越界——見下圖)。與進程虛擬地址的特性一樣,這些虛擬地址與物理內(nèi)存沒有簡單的位移關(guān)系,必須通過內(nèi)核頁表才可轉(zhuǎn)換為物理地址或物理頁。它們有可能尚未被映射,在發(fā)生缺頁時才真正分配物理頁面。
這里給出一個小程序幫助大家認清上面幾種分配函數(shù)所對應(yīng)的區(qū)域。
#include<linux/module.h>
#include<linux/slab.h>
#include<linux/vmalloc.h>
unsigned char *pagemem;
unsigned char *kmallocmem;
unsigned char *vmallocmem;
int init_module(void)
{
pagemem = get_free_page(0);
printk("<1>pagemem=%s",pagemem);
kmallocmem = kmalloc(100,0);
printk("<1>kmallocmem=%s",kmallocmem);
vmallocmem = vmalloc(1000000);
printk("<1>vmallocmem=%s",vmallocmem);
}
void cleanup_module(void)
{
free_page(pagemem);
kfree(kmallocmem);
vfree(vmallocmem);
}
聯(lián)系客服