頁表管理
阿新 • • 發佈:2019-01-27
在不支援PAE的x86機器上,兩層頁表已足夠。中間頁目錄(PMD)被定義成大小為1,它在系統初始化時直接對映為全域性頁目錄(PGD),並在編譯時間以外的時段進行優化。
描述頁目錄
每個程序都有一個指向其自己PGD的指標(mm_struct->pgd
),它其實就是一個物理頁面幀。該幀包括了一個pgd_t
型別的陣列。在x86結構中,程序頁表的載入是通過把mm_struct->pgd
複製到cr3
暫存器完成,這種方式對TLB的重新整理有副作用。事實上,這種方式體現了在不同結構中__flush_tlb()
的實現情況。
描述頁表項
三層頁表中的每一個項PTE
、PMD
、PGD
分別由pte_t
pmd_t
、pgd_t
描述。他們實際上都是無符號的整型資料,之所以定義成結構,一方面是為了起到資料型別保護的作用,以使他們不會被濫用;另一方面是為了滿足某些特性,如在支援PAE的x86中,將有額外的4位用於大於4GB記憶體的定址。 未啟用PAE的x86結構下,定義如下
typedef struct { unsigned long pte_low; } pte_t;
typedef struct { unsigned long pmd; } pmd_t;
typedef struct { unsigned long pgd; } pgd_t;
typedef struct { unsigned long pgprot; } pgprot_t;
在不支援PAE的x86中,pte_t
是一個32位的整形指標。因為每個pte_t
指向一個頁面幀的地址,而所有的被指向的地址都確定是頁對齊的。因此在32為值裡面有12位空閒出來用於描述這個頁表項的狀態位。
Linux不會將PSE位(_PAGE_PROTNONE
)用於使用者頁面,當某個頁面常駐記憶體,且核心不希望使用者訪問的時候,也就是當一個區域通過使用mprotect()
函式設定PROT_NONE
標誌保護起來的時候,實際上是將_PAGE_PRESENT
置為0,將_PAGE_PROTNONE
置為1。當使用巨集pte_present()
檢查這兩位的情況時,只有核心自己知道這個頁是在記憶體中的,但對於使用者是不可訪問的。因為_PAGE_PRESENT
使用頁表項
頁表遍歷操作:
pgd_t *pgd;
pmd_t *pmd;
pte_t *ptep, pte;
pgd=pgd_offset(mm, address);
if(pgd_none(*pgd)||pgd_bad(*pgd))
goto out;
pmd=pmd_offset(pgd, address);
if(pmd_none(*pmd)||pmd_bad(*pmd))
goto out;
ptep=pte_offset(pmd, address);
if(!ptep)
goto out;
pte=*ptep;
核心頁表
對於x86結構,頁表初始化分為兩個階段。bootstrap階段設定頁表使之對映8MB的空間,使得分頁單元能被啟動;第二個階段初始化了剩餘的頁表。
- bootstrap
arch/i386/kernel/head.S中的彙編函式startup_32()
負責啟動分頁單元。vmlinuz
中的所有核心程式碼都是以PAGE_OFFSET+1MiB
為基址編譯的,核心實際上裝載在記憶體的0x00100000處(1MiB)。第一個MiB被用於BIOS與某些裝置通訊。在啟動分頁單元之前,bootstrap中的程式碼通過減去__PAGE_OFFSET
將1MiB視為基址。因此在分頁單元啟動之前,需要建立一個頁表對映轉換8MiB的實體記憶體到虛擬地址的PAGE_OFFSET
。
頁表將虛擬地址的0x00000000~0x00800000和0xc00100000~0xc00900000對映到了0x00100000~0x00900000的實體地址處