1. 程式人生 > >深入理解作業系統虛擬記憶體

深入理解作業系統虛擬記憶體

最近用java NIO的時候涉及到虛擬記憶體的知識,看了網上一些資料感覺寫的都不直觀,決定自己整理一下。

先解釋幾個名詞:

虛擬記憶體

現代作業系統中的程序在使用記憶體的時候,都不是直接訪問記憶體實體地址的,程序訪問的都是虛擬記憶體地址,然後虛擬記憶體地址再轉化為記憶體實體地址。
為什麼要使用虛擬記憶體呢?有以下兩個優點:

  1. 虛擬地址空間可以大於實際的記憶體空間,比如實際記憶體大小是1G,但是虛擬地址空間可以是4G。這樣在作業系統中的普通應用程式看來,就好像是有4G的可用記憶體。
  2. 一個以上的虛擬地址可指向同一個實體記憶體地址。這個很好理解,虛擬地址到實體地址的對映可以是多對一的。

頁面檔案

上文我們說到,虛擬地址空間可以大於實際記憶體空間,這是怎麼實現的呢?很簡單,比如我實際記憶體1G,虛擬記憶體設成了4G,現在往4G的虛擬記憶體裡放了4G的資料,那麼當前只有1G的資料在真實記憶體中,另外的3G因為裝不下就只能以檔案形式放到硬盤裡,這個存放記憶體內容的硬碟檔案就叫頁面檔案。

因此虛擬記憶體的空間=實體記憶體+頁面檔案。

頁面

現在我們知道一個虛擬地址轉化為真實地址的時候,不一定會對應記憶體地址,還可能對應硬碟地址。

而記憶體的一個地址一般對應1byte,而硬碟的一個地址一般對應512byte(一個磁碟扇區),這裡先劇透一下,後面我們會把記憶體和硬盤裡的資料做交換,也就是把一個記憶體地址對應的資料拷貝到硬盤裡或者反過來把硬碟資料拷貝到記憶體裡,因此要方便處理最好統一單位(傳說中的頁對齊)。

頁就是一個統一的單位,頁的大小總是磁碟扇區大小的倍數,通常是2次冪,比如1024、2048、4096位元組。有了頁這個統一單位,接下來我們說的虛擬地址、記憶體地址、磁碟地址都是對應的一個頁。

現在需要的知識點都有了。

現在假設我們是一個程序,我們只知道一個虛擬地址Vaddress1,現在我們要獲取該地址對應的資料,那麼作業系統就應該給我們提供一個這樣的API:

Data getPageByVirtualAddress(Vaddress)

我們要直接通過虛擬地址獲取到資料,至於內部的虛擬地址對映,頁這些概念,作為程序是不需要知道的。

而我們現在的關注點就是,作業系統內部是如何實現這個API的,請看圖:



第一步

第一步是要把虛擬地址Vaddress轉化為實體地址Paddress,這一步是CPU直接完成的,參見維基百科:

Address translation hardware in the CPU, often referred to as a memory
management unit or MMU, automatically translates virtual addresses to
physical addresses.

地址轉換是硬編碼在CPU裡的,CPU裡的MMU負責自動將虛擬地址轉化為實體地址。

這個MMU的工作原理也很簡單,是通過一張頁對映表直接把虛擬地址對映為實體地址。

如圖所示,Vaddress1通過頁對映表被對映為Paddress1,而Paddress1當前指向D1。

第二步

獲取實體地址後,直接通過實體地址去記憶體找資料,只有兩種可能:
1. 找到了,那麼直接返回資料就行了,執行結束。
2. 沒找到,沒找到說明這部分資料存在磁碟的頁面檔案裡了,進入第三步。

在圖中的例子就是沒找到。

第三步

通過實體地址在磁碟頁面檔案找資料:
如圖中所示找到了D1,那麼把D1處的檔案複製到記憶體上的一處空頁R1,然後修改頁對映表中Paddress的指向,由D1改為R1。
返回第一步,重新用虛擬地址去對映實體地址,這次的對映結果將會是R1,然後在第二步的時候就必然會直接從記憶體返回資料。

補充

在第三步中有一個操作是將D1處資料複製到記憶體上的空頁R1,那麼有一種情況是記憶體上的全部頁都已經裝滿資料了,這時怎麼辦呢?

這時就只能去覆蓋一個暫時沒有被使用的記憶體頁,比如R2了。R2上的資料將會被複制到磁碟上的一個空頁D2,然後原本在頁對映表中指向R2的地方將被修改為D2。

那麼問題又來了,上面提到了將R2上的資料複製到磁碟上的一個空頁D2,那麼這時候如果磁碟頁也滿了怎麼辦呢?仔細想想就知道不會發生這種情況的:前面說過

虛擬記憶體的空間=是實體記憶體+頁面檔案。

因此在極限情況下,虛擬記憶體空間全部被佔用,此時實體記憶體和頁面檔案剛好可以存下所有的資料。

總結

從本質上來看,通過使用虛擬記憶體,作業系統將實體記憶體和磁碟分頁檔案這兩者組成了一個分級快取系統。

正在頻繁使用的資料儲存在記憶體中,不頻繁使用的儲存在磁碟上。當要訪問的內容不在記憶體中時,就把硬碟中的內容置換進記憶體,當前沒有被頻繁訪問的內容置換到硬碟。