圖一
上一篇我們已經知道頁目錄如何建立,並且要另外建立一個指向4個頁目錄基底位址的表格,名稱叫作pdp(page-directory-pointer-table)它非常重要,因為我們現在討論的這種2M分頁方式無論如何一定要建立4GB的頁目錄表,並區分成4等分,每一等分佔用4KByte(4096)的空間,也就是說每一等分負責1GB容量的映射(Mapping);這樣的說法不如來作一張圖(如圖一)會更容易理解。雖然圖中例子是共16G記憶體,但可支援到64G的Mapping。
現在我們來看看誰在使用pdp及頁目錄:
int map_page(unsigned long page)
{
unsigned long i;
struct pde
{
unsigned long addr_lo;
unsigned long addr_hi;
};
extern unsigned char pdp[];
extern struct pde pd2[];
unsigned long window = page >> 19;
if (FLAT (window == mapped_window)) { return 0; }
if (window == 0) { return 0; }
if (!v->pae (window >= 32)) {
/* Fail either we don't have pae support
* or we want an address that is out of bounds
* even for pae. */
return -1; }
/* Compute the page table entries... */
//由於左移符號會無法顯示在文章中,因此我把左移使用shl來表示,右移shr,小於lss
for(i = 0; i lss 1024; i++)
{
pd2[i].addr_lo = ((window & 1) shl 31) + ((i & 0x3ff) shl 21) + 0xE3;
pd2[i].addr_hi = (window shr 1);
}
/*以上這個for迴圈會重新建立pd2~pd3的頁目錄表*/
paging_off();
if (window gtr 1) { paging_on(pdp); }
mapped_window = window;
return 0;
}
這個函式可以說是memtest86分頁技術中最經典的routineㄌ。我們來看誰調用它:
有init.c中的cacheable():
/* Ensure the default set of pages are mapped */
map_page(0);
map_page(0x80000);
及main.c中的do_test():
map_page(v->map[0].pbase_addr)
在cacheable()調用它共2個;第一個map_page(0);代入0結果return 0,而且不會去啟用paging_on(pdp);第二個map_page(0x80000);代入0x80000結果也是return 0,而且不會去啟用paging_on(pdp);然而這只是程式的初始,當然不會去啟用分頁;所以真正會不斷調用它的便是map_page(v->map[0].pbase_addr);而且你會發現pd0~pd3好像只有pd2每次都會重新被計算,其實應該說是pd2及pd3每次都被重新計算,因為它的for迴圈是1024次,然而每個pd佔用512個頁目錄表項。所以我們可以開始推論:假設我們主機板插上6GB的記憶體;當測試0~4G時,並不會啟動分頁模式,當測試範圍為4~6G時;便會進入分頁,並且把pd2~pd3對應到4~6G;也就是說此時cpu access的位址雖然是2~4G,但實際上access 到的卻是實體物理位址4~6G。這樣ㄉ推論沒有說服力;所以一定要實際驗證才行。
順便來看一下paging_on:
static void paging_on(void *pdp)
{
if (!v->pae) return;
__asm__ __volatile__(
/* Load the page table address 此處的%0就是pdp的位址 */
"movl %0, %%cr3\n\t"
/* Enable pae cr4的bit5就是pae位元設為1*/
"movl %%cr4, %%eax\n\t"
"orl $0x00000020, %%eax\n\t"
"movl %%eax, %%cr4\n\t"
/* Enable paging cr0的PG位元設為1*/
"movl %%cr0, %%eax\n\t"
"orl $0x80000000, %%eax\n\t"
"movl %%eax, %%cr0\n\t"
:
: "r" (pdp)
: "ax"
);
}
P.S.
試想,為什麼要分頁,而且還分不同的分頁模式,為什麼要把事情搞的那麼複雜ㄋ,難不成就是要存心搞死我們這些人,難道就沒更好的辦法嗎?回顧歷史,32位元CPU已問世多年,我記得Win95那個時候我的系統記憶體也不過才32MB,但是CPU已經可以Access到4G的空間;所以那時RAM遠比CPU定址能力少得多,為了實現虛擬記憶體,讓作業系統可以多工,每個程式都擁有自己的4G位址空間,所以才搞分頁,因為記憶體容量有限,所以要把很多已執行但卻不會馬上用到的行程(Process)所佔用的空間置換(swap)到硬碟(或其他儲存媒體裝置),使正在執行的行程能有足夠的記體體使用權。曾幾何時,CPU定址仍然是32位元,但DRAM價跌容量升級,使很多使用者的安裝於系統的記憶體容量甚至大於4G,因此時代變了分頁技術也要跟著變,就像我們現在所探討的memtest86,就是因為要能符合時代須求所以要測試比4G容量更大的記憶體,且CPU也已經支援這種技術,所以我們才能借助對memtest86的了解,得知這樣的分頁模式。總之這樣的分頁模式和現行OS所實作的分頁稍有不同,就是因為須求不同的原因;或許未來OS設計會將資訊全數載入記憶體,便不需要swap到硬碟,透過PAE這樣的分頁方式來取得資訊,我想一定能更加速系統的運作,但先決條件是DRAM還要更便宜,容量再加大;純假設。
待續‧‧‧