學習於:《深入理解計算機系統》

前面講了很多次頁表,發現之前的都太模型化了,也就是我們只使用了一個單獨的頁表來進行翻譯。

頁表就是一個頁表條目(Page Table Entry,

PTE

)的陣列。頁表條目就包括:哪一頁,是否在記憶體,虛擬記憶體中的地址,在記憶體中的地址,在外存中的地址這類資訊。下面是我們之前使用的頁表。

linux 多級頁表 cpu取虛擬地址 TLB

然後,如果是這樣的話,假如4G的虛擬地址,每條佔4個位元組。1個page為4kb。如下圖:

linux 多級頁表 cpu取虛擬地址 TLB

4G/4KB=1M ,所以就會有1M個條目,而每個條目4個位元組,所以這就需要4M個位元組儲存這個頁表。

這意味著什麼? 無論我們程序實際使用多大的虛擬記憶體,都要有著4M的頁表儲存在記憶體中。這是比較浪費的。當系統變成64位的時候,就變得極其恐怖。

然後我們來看一個簡單的二級頁表先。

首先,一級頁表中的每個PTE負責對映虛擬地址空間中一個4MB的片(chunk),這裡

每一片都是由1024個連續的頁面組成的

。比如,PTE0對映第一片,PTE1對映接下來的一片,以此類推。假設地址空間是4G,一級有1024個PTE已經足夠覆蓋整個空間了。

(即1024x4M=4G,其中4M=1024x4kb)

如果片i的每個頁面都未被分配,那麼一級PTEi就為空。例如,下圖中的片2~7都是未分配的。然而,如果在片i中至少有一個頁是分配了得,那麼一級PTEi就指向一個二級頁表的基址。例如下圖,片0、1和8的所有或者部分已被分配,所以他們的一級PTE就指向二級頁表。

下面直接盜圖於書中,知乎是不可能把圖片正常顯示的了,所以理解前面這段話再稍微瞄一眼就好。或者下載下來另外觀看。

linux 多級頁表 cpu取虛擬地址 TLB

所以,注意,這裡並不是直接一級頁表就可以找到對應的虛擬記憶體了,必須要經過二級頁表!!!

二級頁表中的每個PTE都負責對映一個4KB的虛擬記憶體頁面,就像我們檢視只有一級頁表一樣。

注意,使用4位元組的PTE,每個一級和二級頁表都是4KB位元組,這剛好和一個頁面的大小是一樣的。

這種方法從兩個方面減少了記憶體的要求。

第一:如果一級頁表中的一個PTE是空的,那麼相應的二級頁表就根本不會存在。這代表著一種巨大的潛在節約,因為對於一個典型的程式,4GB的虛擬地址空間的大部分都會是未分配的。

第二:只有一級頁表才需要總是在主存中;虛擬記憶體系統可以在需要時建立、頁面調入或調出二級頁表,這就減少了主存的壓力;只有最經常使用的二級頁表才需要快取在主存中。

前面的文章中層提到過實際使用的虛擬記憶體並沒有4G的問題,當時沒有考慮,現在結合這裡的理論看看那圖:ps aux 可得

linux 多級頁表 cpu取虛擬地址 TLB

這裡是以kb為單位,我們可以看到,虛擬記憶體使用最多的還是java程序的260M這樣。然後再結合前面的圖,虛擬記憶體中,程式碼和資料段佔據起始地址很小的地方,接近3g的地址處還有個棧,環境變數啥的。中間如果你不建立多一些物件,很大一部分都是空的,對了,還有個共享庫,一般來說都是堆比較大吧。到現在,是不是對底層的許多東西有了更深的認識啊?

然後到實際上,unix一般是3級,linux一般是4級,看版本吧。反正最後一級才是指向虛擬記憶體的地址,前面的全是索引。訪問k個PTE,第一眼看上去昂貴而不切實際。然而,這裡TLB能夠起作用,正是透過將不同層次上頁表的PTE快取起來。實際上,帶多級頁表的地址翻譯並不比單級頁錶慢很多。但是,多級頁表減少了記憶體的壓力。

不知道大家還記得TLB嗎?前面的文章介紹過的,即翻譯後備緩衝器(Translation Lookaside Buffer ,TLB),就是在 何柄融:作業系統 虛擬記憶體技術 這篇文章中講的快表,利用了局部性原理。 實際上,它是一個小的、虛擬定址的快取,其中每一行都儲存著一個由單個PTE組成的塊。總之裡面就是把你常用的PTE留在了那裡,不需要每次都從外面的快取或者主存中取。

這裡用書中的語言再描述一次cpu取址的步驟:

1。cpu產生一個虛擬地址

2。MMU從TLB中取出相應的PTE。

3。MMU將這個虛擬地址翻譯成一個物理地址,並且將它傳送到快取記憶體/主存。

4。快取記憶體/主存將所請求的資料字返回給CPU。

當TLB不命中,MMU必須從L1快取中取出相應的PTE。新取出的PTE存放在TLB中,可能會覆蓋一個已經存在的條目。(這個看具體原始碼了)

最後上個圖:

linux 多級頁表 cpu取虛擬地址 TLB

注意TLB是在cpu內部喔! 然後剩下的大家慢慢領悟。

嗯,感覺到這裡就差不多了。

歡迎交流討論。