1. 程式人生 > >linux記憶體管理---實體地址、線性地址、虛擬地址、邏輯地址之間的轉換

linux記憶體管理---實體地址、線性地址、虛擬地址、邏輯地址之間的轉換

CPU的頁式記憶體管理單元,負責把一個線性地址,最終翻譯為一個實體地址。從管理和效率的角度出發,線性地址被分為以固定長度為單位的組,稱為頁(page),例如一個32位的機器,線性地址最大可為4G,可以用4KB為一個頁來劃分,這頁,整個線性地址就被劃分為一個tatol_page[2^20]的大陣列,共有2的20個次方個頁。這個大陣列我們稱之為頁目錄。目錄中的每一個目錄項,就是一個地址——對應的頁的地址。

另一類“頁”,我們稱之為物理頁,或者是頁框、頁楨的。是分頁單元把所有的實體記憶體也劃分為固定長度的管理單位,它的長度一般與記憶體頁是一一對應的。

這裡注意到,這個total_page陣列有2^20個成員,每個成員是一個地址(32位機,一個地址也就是4位元組),那麼要單單要表示這麼一個數組,就要佔去4MB的記憶體空間。為了節省空間,引入了一個二級管理模式的機器來組織分頁單元。文字描述太累,看圖直觀一些:

Snap1.jpg
如上圖,
1、分頁單元中,頁目錄是唯一的,它的地址放在CPU的cr3暫存器中,是進行地址轉換的開始點。萬里長征就從此長始了。
2、每一個活動的程序,因為都有其獨立的對應的虛似記憶體(頁目錄也是唯一的),那麼它也對應了一個獨立的頁目錄地址。——執行一個程序,需要將它的頁目錄地址放到cr3暫存器中,將別個的儲存下來。
3、每一個32位的線性地址被劃分為三部份,頁目錄索引(10位):頁表索引(10位):偏移(12位)
依據以下步驟進行轉換:
1、從cr3中取出程序的頁目錄地址(作業系統負責在排程程序的時候,把這個地址裝入對應暫存器);
2、根據線性地址前十位,在陣列中,找到對應的索引項,因為引入了二級管理模式,頁目錄中的項,不再是頁的地址,而是一個頁表的地址。(又引入了一個數組),頁的地址被放到頁表中去了。

3、根據線性地址的中間十位,在頁表(也是陣列)中找到頁的起始地址;
4、將頁的起始地址與線性地址中最後12位相加,得到最終我們想要的葫蘆;

這個轉換過程,應該說還是非常簡單地。全部由硬體完成,雖然多了一道手續,但是節約了大量的記憶體,還是值得的。那麼再簡單地驗證一下:
1、這樣的二級模式是否仍能夠表示4G的地址;
頁目錄共有:2^10項,也就是說有這麼多個頁表
每個目表對應了:2^10頁;
每個頁中可定址:2^12個位元組。
還是2^32 = 4GB

2、這樣的二級模式是否真的節約了空間;
按<深入理解計算機系統>中的解釋,二級模式空間的節約是從兩個方面實現的:
A、如果一級頁表中的一個頁表條目為空,那麼那所指的二級頁表就根本不會存在。這表現出一種巨大的潛在節約,因為對於一個典型的程式,4GB虛擬地址空間的大部份都會是未分配的;

B、只有一級頁表才需要總是在主存中。虛擬儲存器系統可以在需要時建立,並頁面調入或調出二級頁表,這就減少了主存的壓力。只有最經常使用的二級頁表才需要快取在主存中。——不過Linux並沒有完全享受這種福利,它的頁表目錄和與已分配頁面相關的頁表都是常駐記憶體的。

值得一提的是,雖然頁目錄和頁表中的項,都是4個位元組,32位,但是它們都只用高20位,低12位遮蔽為0——把頁表的低12遮蔽為0,是很好理解的,因為這樣,它剛好和一個頁面大小對應起來,大家都成整數增加。計算起來就方便多了。但是,為什麼同時也要把頁目錄低12位遮蔽掉呢?因為按同樣的道理,只要遮蔽其低10位就可以了,不過我想,因為12>10,這樣,可以讓頁目錄和頁表使用相同的資料結構,方便。

本貼只介紹一般性轉換的原理,擴充套件分頁、頁的保護機制、PAE模式的分頁這些麻煩點的東東就不囉嗦了……可以參考其它專業書籍。