歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> Linux內核

Linux內核中的內存

內核把物理頁作為內存管理的基本單位,盡管處理器的最小可尋址單位通常為字節,但是MMU(內存管理單元,管理內存並把虛擬地址轉換為物理地址的硬件)通常以頁作為單位進行處理。大多數32位體系結構支持4KB的頁,內核用struct page結構表示系統的物理頁,這個page與物理頁相關,而並非與虛擬頁相關。頁的擁有者可能是用戶空間進程、動態分配的內核數據、靜態內核代碼或頁高速緩存等。

 

由於有些頁位於內存特定的物理地址上,所以不能將其用於一些特定的任務。故內核把頁分成不同的區,分別是ZONE_DMA、ZONE_DMA32、ZONE_NORMAL和ZONE_HIGHMEM(高端內存,其中的頁並不能永久地映射到內核地址空間)。每個區都用struct zone結構體表示,對於32位的X86上,ZONE_DMA包含的頁在0-16MB的內存范圍裡,ZONE_NORMAL是從16MB到896MB的所有物理內存,ZONE_HIGHMEM是高於896MB的所有物理內存。需要注意的是,不是所有的體系結構都定義了全部區,比如X86-64沒有ZONE_HIGHMEM區。

 

如果需要以頁為單位的一族連續物理頁時,尤其只需要一兩頁時,有些低級頁函數很有用。alloc_page(返回頁結構指針)+page_address=__get_free_pages(返回邏輯地址指針)。

 

如果需要連續的物理頁,除了使用上面說的低級頁函數外,還可以使用kmalloc函數,傳遞給這個函數的常用標志GFP_KERNEL和GPF_ATOMIC。其中GPF_ATOMIC用在中斷處理程序、下半部、持有自旋鎖以及其他不能睡眠的地方;GPF_KERNEL是常用分配方法,可能會阻塞,在睡眠安全時用在進程上下文中;GPF_NOIO這種分配可以阻塞,但不會啟動磁盤I/O。

 

如果不需要連續的物理地址,只要連續的虛擬地址,可以使用vmalloc函數,但是由於物理地址的不連續性,導致通過vmalloc獲得的頁必須一個個的進行映射,這就導致比直接內存映射大的多的TLB(轉換旁路緩存,用來緩存虛擬地址到物理地址的映射關系)抖動,所以,迫不得已不用該函數,典型的就是為了獲得大塊內存時,比如模塊動態加載。

 

如果需要從高端內存進行分配,我們知道高端內存(物理地址高於896MB)中的頁被映射到3GB-4GB上,我們應該使用alloc_pages獲得頁指針,而不能用__get_free_pages或kmalloc,因為這兩個函數返回的都是邏輯地址,而不是page結構,這兩個函數分配的內存當前有可能還沒有映射到內核的虛擬空間。如果需要將獲得的頁永久映射到內核地址空間,可以使用kmap函數,這個函數可以睡眠,因此只能在進程上下文使用,因為允許永久映射的數量是有限的,所以當不需要高端內存時,應該用kunmap解除映射。如果需要獲得的頁臨時映射到內核地址空間,可以使用kmap_atomic函數,它不能睡眠,可以用在中斷處理程序中,同樣釋放用kunmap_atomic函數。

 

如果需要創建和撤銷很多大的數據結構,就需要考慮建立slab高速緩存了,它能極大提高對象分配和回收的性能。Slab層不是頻繁地分配和釋放內存,而是為你把事先分配好的對象存放到高速緩存中,當你需要一塊新的內存來存放數據結構時,slab層一般無須另外去分配內存,而只需要從高速緩存中得到這個對象。

 

Slab層把不同的對象分為高速緩存組,每個高速緩存組中存放不同類型的對象,也就是每個對象對應一個高速緩存。每一個高速緩存又被劃分成多個slab,slab由一個或多個物理上連續的頁組成。每個slab處於三種狀態之一:滿、部分滿或空,當內核的某一部分需要一個新的對象時,先從部分滿的slab中進行分配,如果沒有部分滿的slab,就從空的slab中進行分配,如果沒有空的slab,就要創建一個slab,這樣能減少碎片。每一個高速緩存用struct kmem_cache結構體表示,同時slab也有struct slab結構體。

 

Slab分配器的使用

struct kmem_cache *task_struct_cachep;

task_struct_cachep=kmem_cache_create(“task_struct”,sizeof(task_struct),ARCH_MIN_TASKALIGN,SLAB_PANIC|SLAB_NOTRACK,NULL); //創建高速緩存

struct task_struct *tsk;

tsk=kmem_cache_alloc(task_struct_cachep,GPF_KERNEL); //從緩存中分配

…..

kmem_cache_free(task_struct_cachep,tsk);  //從緩存中釋放

kmem_cache_destroy(task_struct_cachep);  //撤銷高速緩存

 

內核棧

每個進程都有兩個頁的內核棧,32位和64位體系結構的頁面大小分別是4KB和8KB,所以內核棧的大小分別是8KB和16KB。由於連續兩頁有時難以尋找到,所以引入單頁內核棧,當我們使用只有一個頁面的內核棧的時候,中斷處理程序就不放在棧裡了,我們為每個進程提供一個用於中斷處理程序的棧,即中斷棧,這樣中斷處理程序不用再和被中斷進程共享一個內核棧。總的來說,內核棧可以是1頁頁可以是2頁,棧的大小因此在4-16KB的范圍,歷史上,中斷處理程序和被中斷的進程共享一個棧,不過當1頁棧被激活,中斷處理程序就獲得自己的棧。

Copyright © Linux教程網 All Rights Reserved