(筆記)Linux內核學習(九)之內核內存管理方式
一 頁
內核把物理頁作為內存管理的基本單位;內存管理單元(MMU)把虛擬地址轉換為物理
地址,通常以頁為單位進行處理。MMU以頁大小為單位來管理系統中的也表。
32位系統:頁大小4KB
64位系統:頁大小8KB
內核用相應的數據結構表示系統中的每個物理頁:
<linux/mm_types.h>
struct page {}
內核通過這樣的數據結構管理系統中所有的頁,因此內核判斷一個頁是否空閑,誰有擁有這個頁
,擁有者可能是:用戶空間進程、動態分配的內核數據、靜態內核代碼、頁高速緩存……
系統中每一個物理頁都要分配這樣一個結構體,進行內存管理。
二 區
Linux內存尋址存在問題:
一些硬件只能用某些特定的內存來執行DMA(直接內存訪問)
一些體系結構其內存的物理尋址範圍必須你尋址範圍大得多。這樣導致一些內存不能永久映射到內核空間上。
通常32位Linux內核地址空間劃分0~3G為用戶空間,3~4G為內核空間。當內核模塊代碼或線程訪問內存時,
代碼中的內存地址都為邏輯地址,而對應到真正的物理內存地址,需要地址一對一的映射。因此內核空間地址為3~4G,
最多只能映射到1G空間的內存,超出1G大小的內存將如何去問呢!
由於存在上述條件的限制。Linux將內核空間地址劃分為三個區:
ZONE_DMA、ZONE_NORMAL和ZONE_HIGHMEM。
ZONE_HIGHMEM即為高端內存,這就是內存高端內存概念的由來。
在x86結構中,三種類型的區域如下:
ZONE_DMA 內存開始的16MB
ZONE_NORMAL 16MB~896MB
ZONE_HIGHMEM 896MB ~ 結束
同樣每個區包含眾多頁,形成不同內存池,按照用途進行內存分配。
用相應的數據結構來表示區:
<linux/mmzone.h>
struct zone {}
三 獲取頁/內存
static inline struct page *alloc_pages(gfp_t gfp_mask, unsigned int order)
該函數分配2的order次方個連續的物理頁,返回指向第一個頁的page結構體指針。
void *page_address(const struct page *page)
返回指向給定物理頁當前所在的邏輯地址
extern unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order);
extern unsigned long get_zeroed_page(gfp_t gfp_mask);
釋放:
extern void __free_pages(struct page *page, unsigned int order);
extern void free_pages(unsigned long addr, unsigned int order);
內存的分配可能失敗,內存的釋放要準確!
1 kmalloc
kmalloc()函數與用戶空間malloc一組函數類似,獲得以字節為單位的一塊內核內存。
void *kmalloc(size_t size, gfp_t flags)
void kfree(const void *objp)
分配內存物理上連續。
gfp_t標誌:表明分配內存的方式。如:
GFP_ATOMIC:分配內存優先級高,不會睡眠
GFP_KERNEL:常用的方式,可能會阻塞。
2 vmalloc
void *vmalloc(unsigned long size)
void vfree(const void *addr)
vmalloc()與kmalloc方式類似,vmalloc分配的內存虛擬地址是連續的,而物理地址則無需連續,與用戶空間分配函數一致。
vmalloc通過分配非連續的物理內存塊,在修正頁表,把內存映射到邏輯地址空間的連續區域中,虛擬地址是連續的。
是否必須要連續的物理地址和具體使用場景有關。在不理解虛擬地址的硬件設備中,內存區都必須是連續的。
通過建立頁表轉換成虛擬地址空間上連續,肯定存在一些消耗,帶來性能上影響。
所以通常內核使用kmalloc來申請內存,在需要大塊內存時使用vmalloc來分配。
四 slab層
內核中經常進行內存的分配和釋放。為了便於數據的頻繁分配和回收,通常建立一個空
閑鏈表——內存池。當不使用的已分配的內存時,將其放入內存池中,而不是直接釋放掉。
Linux內核提供了slab層來管理內存的分配和釋放。
頻繁分配和回收必然導致內存碎片,緩存他們.
slab層得設計實現
slab層把不同的對象劃分為所謂的高速緩存組。每個高速緩存組存放不同類型的對象。高速緩存劃分為slab,
slab由一個或多個物理上連續的頁組成。每個slab處於三種狀態之一:滿,部分滿,空。
高速緩存,slab,對象之間的關系:
與傳統的內存管理模式相比, slab 緩存分配器提供了很多優點。首先,內核通常依賴於對小對象的分配,
它們會在系統生命周期內進行無數次分配。slab 緩存分配器通過對類似大小的對象進行緩存而提供這種功能,
從而避免了常見的碎片問題。slab 分配器還支持通用對象的初始化,從而避免了為同一目而對一個對象重復
進行初始化。最後,slab 分配器還可以支持硬件緩存對齊和著色,這允許不同緩存中的對象占用相同的緩存行,
從而提高緩存的利用率並獲得更好的性能。
slab數據結構和接口:
每個高速緩存用kmem_cache結構來表示:
struct kmem_cache {
struct kmem_list3 **nodelists;
……
}
緩存區包含三種slab:滿,未滿,空閑
struct kmem_list3 {
struct list_head slabs_partial; /* partial list first, better asm code */
struct list_head slabs_full;
struct list_head slabs_free;
……
};
每一個slab包含多個對象:
struct slab {
struct list_head list;
unsigned long colouroff;
void *s_mem; /* including colour offset */
unsigned int inuse; /* num of objs active in slab */
kmem_bufctl_t free;
unsigned short nodeid;
};
相關接口:mm/slab.c
內核函數 kmem_cache_create 用來創建一個新緩存。這通常是在內核初始化時執行的,或者在首次加載內核模塊時執行。
struct kmem_cache *kmem_cache_create (
const char *name,
size_t size,
size_t align,
unsigned long flags,
void (*ctor)(void *))
name 參數定義了緩存名稱,proc 文件系統(在 /proc/slabinfo 中)使用它標識這個緩存。
size 參數指定了為這個緩存創建的對象的大小,
align 參數定義了每個對象必需的對齊。
flags 參數指定了為緩存啟用的選項:
kmem_cache_create 的部分選項(在 flags 參數中指定)
SLAB_RED_ZONE 在對象頭、尾插入標誌,用來支持對緩沖區溢出的檢查。
SLAB_POISON 使用一種己知模式填充 slab,允許對緩存中的對象進行監視(對象屬對象所有,不過可以在外部進行修改)。
SLAB_HWCACHE_ALIGN 指定緩存對象必須與硬件緩存行對齊。
ctor 和 dtor 參數定義了一個可選的對象構造器和析構器。構造器和析構器是用戶提供的回調函數。當從緩存中分配新對象時,可以通過構造器進行初始化。
要從一個命名的緩存中分配一個對象,可以使用 kmem_cache_alloc 函數。
void kmem_cache_alloc( struct kmem_cache *cachep, gfp_t flags );
這個函數從緩存中返回一個對象。註意如果緩存目前為空,那麽這個函數就會調用 cache_alloc_refill 向緩存中增加內存。
kmem_cache_alloc 的 flags 選項與 kmalloc 的
cachep:所建立的緩存區
flags參數:
GFP_USER 為用戶分配內存(這個調用可能會睡眠)。
GFP_KERNEL 從內核 RAM 中分配內存(這個調用可能會睡眠)。
GFP_ATOMIC 使該調用強制處於非睡眠狀態(對中斷處理程序非常有用)。
GFP_HIGHUSER 從高端內存中分配內存。
五 高端內存的映射
永久映射:可能會阻塞
映射一個給定的page結構到內核地址空間:
void *kmap(struct page *page)
解除映射:
void kunmap(struct page *page)
臨時映射:不會阻塞
void *kmap_atomic(struct page *page)
六 分配函數的選擇
l 連續的物理頁:kmalloc或者低級頁分配器
l 高端內存分配:alloc_pages 指向page結構指針,不是邏輯地址指針。再通過kmap()把高端地址內存映射到內核的邏輯地址空間。
l 無需連續物理地址:vmalloc 虛擬地址連續物理地址可能不連續,相對存在性能損失
l 頻繁創建和銷毀很多較大數據結構:建立slab緩存區,提高對象分配和回收性能。
Linux高端內存:
http://ilinuxkernel.com/?p=1013
Linux slab 分配器剖析:
https://www.ibm.com/developerworks/cn/linux/l-linux-slab-allocator/
(筆記)Linux內核學習(九)之內核內存管理方式