Linux 段頁式記憶體管理
分段機制
分段(Segmentation):這種方法是人們最開始使用的一種方法,基本思路是將程式所需要的記憶體地址空間大小的虛擬空間對映到某個實體地址空間。
每個程式都有其獨立的虛擬的獨立的程序地址空間,可以看到程式A和B的虛擬地址空間都是從0x00000000開始的。我們將兩塊大小相同的虛擬地址空間和實際實體地址空間一一對映,即虛擬地址空間中的每個位元組對應於實際地址空間中的每個位元組,這個對映過程由軟體來設定對映的機制,實際的轉換由硬體來完成。
這種分段的機制解決了文章一開始提到的3個問題中的程序地址空間隔離和程式地址重定位的問題。程式A和程式B有自己獨立的虛擬地址空間,而且該虛擬地址空間被對映到了互相不重疊的實體地址空間,如果程式A訪問虛擬地址空間的地址不在0x00000000-0x00A00000這個範圍內,那麼核心就會拒絕這個請求,所以它解決了隔離地址空間的問題。我們應用程式A只需要關心其虛擬地址空間0x00000000-0x00A00000,而其被對映到哪個實體地址我們無需關心,所以程式永遠按照這個虛擬地址空間來放置變數,程式碼,不需要重新定位。
無論如何分段機制解決了上面兩個問題,是一個很大的進步,但是對於記憶體效率問題仍然無能為力。因為這種記憶體對映機制仍然是以程式為單位,當記憶體不足時仍然需要將整個程式交換到磁碟,這樣記憶體使用的效率仍然很低。那麼,怎麼才算高效率的記憶體使用呢。事實上,根據程式的區域性性執行原理,一個程式在執行的過程當中,在某個時間段內,只有一小部分資料會被經常用到。所以我們需要更加小粒度的記憶體分割和對映方法,此時是否會想到Linux中的Buddy演算法和slab記憶體分配機制呢。
分頁機制
分頁機制就是把記憶體地址空間分為若干個很小的固定大小的頁,每一頁的大小由記憶體決定,就像Linux中ext檔案系統將磁碟分成若干個Block一樣,這樣做是分別是為了提高記憶體和磁碟的利用率。試想一下,如果將磁碟空間分成N等份,每一份的大小(一個Block)是1M,如果我想儲存在磁碟上的檔案是1K位元組,那麼其餘的999位元組是不是浪費了。所以需要更加細粒度的磁碟分割方式,我們可以將Block設定得小一點。
Linux中一般頁的大小是4KB,我們把程序的地址空間按頁分割,把常用的資料和內碼表裝載到記憶體中,不常用的程式碼和資料儲存在磁碟中,我們還是以一個例子來說明,如下圖:
程序虛擬地址空間、實體地址空間和磁碟之間的頁對映關係
我們可以看到程序1和程序2的虛擬地址空間都被對映到了不連續的實體地址空間內(這個意義很大,如果有一天我們的連續實體地址空間不夠,但是不連續的地址空間很多,如果沒有這種技術,我們的程式就沒有辦法執行),甚至他們共用了一部分實體地址空間,這就是共享記憶體。
程序1的虛擬頁VP2和VP3被交換到了磁碟中,在程式需要這兩頁的時候,Linux核心會產生一個缺頁異常,然後異常管理程式會將其讀到記憶體中。
這就是分頁機制的原理,當然Linux中的分頁機制的實現還是比較複雜的,通過了頁全域性目錄,頁上級目錄,頁中級目錄,頁表等幾級的分頁機制來實現的,但是基本的工作原理是不會變的。
分頁機制的實現需要硬體的實現,這個硬體名字叫做MMU(Memory Management Unit),他就是專門負責從虛擬地址到實體地址轉換的,也就是從虛擬頁找到物理頁。