內核把物理頁作為內存管理的基本單位;內存管理單元(MMU,管理內存並把虛擬地址轉換為物理地址)通常以頁為單位進行處理。MMU以頁大小為單位來管理系統中的頁表。從虛擬內存的角度看,頁就是最小單位。
32位系統:頁大小4KB
64位系統:頁大小8KB
在支持4KB頁大小並有1GB物理內存的機器上,物理內存會被劃分為262144個頁。內核用 struct page 結構表示系統中的每個物理頁。
struct page {
page_flags_t flags; /* 表示頁的狀態,每一位表示一種狀態*/
atomic_t _count; /* 存放頁的引用計數,0代表沒有被引用 */
atomic_t _mapcount;
unsigned long private;
strcut address_space *mapping;
pgoff_t index;
struct list_head lru;
void *virtual; /* 頁在虛擬內存中的地址,動態映射物理頁 */
}
下面,我們來解釋下其中的重要字段。
flags:這個字段用於存放頁的狀態。這些狀態包括頁是不是髒的,是不是被鎖定在內存中等。 flag 的每一位單獨表示一種狀態,所以,它至少可以同時表示出32種不同的狀態。
_count:這個字段存放頁的使用計數,也就是這個頁被引用了多少次。很奇怪,技術值變為 -1 時,就說明當前內核並沒有引用這一頁,於是,在新的分配中就可以使用它,注意,這個字段使用的是 -1 代表未使用,而不是 0 。
virtual:這個字段是頁的虛擬地址。
mapping:這個域指向和這個頁關聯的address_space 對象。
private:這個根據名字就可以看得出,它指向私有數據。
內核通過這樣的數據結構管理系統中所有的頁,因為內核需要知道一個頁是否空閒,誰有擁有這個頁。擁有者可能是:用戶空間進程、動態分配的內核數據、靜態內核代碼、頁高速緩存等等。系統中每一個物理頁都要分配這樣一個結構體,進行內存管理。
Linux基礎篇之內存管理機制 http://www.linuxidc.com/Linux/2014-03/98293.htm
由於硬件的限制,內核並不能對所有的頁一視同仁。Linux必須處理如下兩種由於硬件存在缺陷而引起的內存尋址問題:
1)一些硬件只能用某些特定的內存地址來執行DMA(直接內存訪問)。
2)一些體系結構其內存的物理尋址范圍比虛擬尋址范圍大得多。這樣,就有一些內存不能永久地映射到內核空間上。
由於存在這種限制,內核把具有相似特性的頁劃分為不同的區(ZONE):
1)ZONE_DMA——這個區包含的頁能用來執行DMA操作。
2)ZONE_NORMAL——這個區包含的都是能正常地映射網頁。
3)ZONE_DMA32——同上,不過只能被32位設備訪問
4)ZONE_HIGHMEM——這個區包含“高端內存”,其中的頁並能不永久地映射到內核地址空間。
Linux把系統的頁劃分為區,形成不同的內存池,這樣就可以根據用途進行分配。注意,區的劃分沒有任何物理意義,這只是內核為了管理頁而采取的一種邏輯上的分組。用於DMA的內存必須從ZONE_DMA中進行分配,但是一般用途的內存卻既能從ZONE_DMA分配,也能從ZONE_NORMAL分配。
內核提供了一種請求內存的底層機制,並提供了對它進行訪問的幾個接口。所有這些接口都以頁為單位分配內存,定義於<linux/gfp.h>。最核心的函數是:
structpage *alloc_pages( unsigned int gfp_mask, unsigned int order );
該函數分配 2order 個連續的物理頁,並返回一個指向第一頁的 page 結構體指針,如果出錯就返回NULL。
void*page_address( struct page *page );
把給定的頁轉換成它的邏輯地址。如果無須用到 struct page,可以調用:
unsignedlong __get_free_pages( unsigned int gfp_mask, unsigned int order );
這個函數與alloc_pages 作用相同,不過它直接返回所請求的第一個頁的邏輯地址。因為頁是連續的,因此其他頁也會緊隨其後。
如果只需要一頁,可以用以下兩個函數:
structpage *alloc_page( unsigned int gfp_mask );
unsignedlong _get_free_page( unsigned int gfp_mask );
如果需要讓返回頁的內容全為0,可以使用下面這個函數
unsignedlong get_zeroed_page(unsigned int gfp_mask );
方法
描述
alloc_page(gfp_mask)
只分配一頁,返回指向頁結構的指針
alloc_pages(gfp_mask, order)
分配 2^order 個頁,返回指向第一頁頁結構的指針
__get_free_page(gfp_mask)
只分配一頁,返回指向其邏輯地址的指針
__get_free_pages(gfp_mask, order)
分配 2^order 個頁,返回指向第一頁邏輯地址的指針
get_zeroed_page(gfp_mask)
只分配一頁,讓其內容填充為0,返回指向其邏輯地址的指針
當不再需要頁時可以使用以下函數來釋放它。
void__free_pages( struct page *page, unsigned int order );
voidfree_pages( unsigned long addr, unsigned int order );
voidfree_page( unsigned long addr );
釋放頁時要謹慎,只能釋放屬於你的頁。傳遞了錯誤的 struct page 或地址,用了錯誤的 order 值都可能導致系統崩潰。請記住,內核是完全依賴自己的。
更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2014-08/105365p2.htm