linux中使用三級頁表完成地址轉換。利用多級頁表能夠節約地址轉換需要佔用的存放空間。如果利用三級頁錶轉換地址,即使是64位機佔用的空間也有限。Linux對所有體系結構,包括那些不支持三級頁表的體系結構都使用三級頁表管理,因爲使用三級頁表結構可以利用“最大公約數”的思想。
頂級頁表是頁全局目錄(PGD)。PGD包含了一個pgd_t類型數組,多數體系結構中pgd_t類型等同於無符號長整型類型。PGD中的頁表項指向二級頁目錄中的表項:PMD。
二級頁表是中間頁目錄(PMD)。PMD是個pmd_t類型數組,其中的表項指向PTE中的表項。
最後一級的頁表稱爲頁表,其中包含pte_t類型的頁表項,該表項指向物理頁面。
頁表對應的結構體依賴於具體的體系結構,所以定義在asm/page.h中。
- 在文件include/asm-i386/page.h中
- #ifdef CONFIG_X86_PAE
- extern unsigned long long __supported_pte_mask;
- typedef struct { unsigned long pte_low, pte_high; } pte_t;
- typedef struct { unsigned long long pmd; } pmd_t;
- typedef struct { unsigned long long pgd; } pgd_t;
- typedef struct { unsigned long long pgprot; } pgprot_t;
- #define pmd_val(x) ((x).pmd)
- #define pte_val(x) ((x).pte_low | ((unsigned long long)(x).pte_high << 32))
- #define __pmd(x) ((pmd_t) { (x) } )
- #define HPAGE_SHIFT 21
- #include <asm-generic/pgtable-nopud.h>
- #else
- typedef struct { unsigned long pte_low; } pte_t;
- typedef struct { unsigned long pgd; } pgd_t;
- typedef struct { unsigned long pgprot; } pgprot_t;
- #define boot_pte_t pte_t /* or would you rather have a typedef */
- #define pte_val(x) ((x).pte_low)
- #define HPAGE_SHIFT 22
- #include <asm-generic/pgtable-nopmd.h>
- #endif
多數體系結構中,搜索頁表的工作由硬件完成。雖然通常操作中,很多使用頁表的工作都可以由硬件執行,但是隻有在內核正確設置頁表的前提下,硬件才能方便操作它們。
Linux總是假定處理器有三級頁表。每個頁表通過所包含的下級頁表的頁面框號來訪問。下圖給出了虛擬地址是如何分割成多個域的,每個域提供了 某個指定頁表的偏移。爲了將虛擬地址轉換成物理地址,處理器必須得到每個域的值。這個過程將持續三次直到對應於虛擬地址的物理頁面框號被找到。最後再使用 虛擬地址中的最後一個域,得到了頁面中數據的地址。
爲了實現跨平臺運行,Linux提供了一系列轉換宏使得核心可以訪問特定進程的頁表。這樣核心無需知道 頁表入口的結構以及它們的排列方式。
這種策略相當成功,無論在具有三級頁表結構的Alpha AXP還是兩級頁表的Intel X86處理器中,Linux總是使 用相同的頁表操縱代碼。
Linux的三級頁表結構
每個進程都有自己的頁表(線程會共享頁表)。內存描述符的pgd域指向的就是進程的頁全局目錄。操作和檢索頁表時必須使用page_table_lock鎖,該鎖在相應的進程的內存描述符中,以防止競爭條件。
由於幾乎每次對虛擬內存中的頁面訪問都必須先解析它,然後得到物理內存中對應的地址,所以頁表的操作性能很關鍵。爲了加快搜索,多數體系結構實現了 TLB(translation lookside buffer)。TLB作爲一個將虛擬地址映射到物理地址的硬件緩存,當請求訪問一個虛擬地址時,處理器將首先檢查TLB中十分緩存了該虛擬地址到物理地 址的映射,如果在緩衝中直接命中,物理地址立刻返回;未命中,就需要再通過頁表搜索需要的物理地址。
2.6內核對頁表管理的主要改進是:從高端內存分配部分頁表。今後可能的改進包括通過在寫時拷貝(copy-on-write) 的方式共享頁表。這種機制似的在fork()操作中可由父子進程共享頁表。因爲只有當子進程或父進程試圖修改特定頁表項時,內核纔去創建該頁表項的新拷 貝,此後父子進程纔不再共享該頁表項。利用共享頁表可以消除fork()操作中頁表拷貝所帶來的消耗。