歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> 我對虛擬內存的理解

我對虛擬內存的理解

日期:2017/2/28 14:22:54   编辑:Linux教程

什麼是虛擬內存?

先直接摘抄一段 wikipedia 上的介紹。

虛擬內存是計算機系統內存管理的一種技術。它使得應用程序認為它擁有連續的可用的內存(一個連續完整的地址空間),而實際上,它通常是被分隔成多個物理內存碎片,還有部分暫時存儲在外部磁盤存儲器上,在需要時進行數據交換。

對於 C 語言裡面的變量,我們可以使用 & 運算符來獲得其地址, 既然是虛擬地址,就是指這個地址是虛擬的。

虛擬地址機制不是必須的,在簡單的單片機中,編寫的代碼編譯時都需要指定物理 RAM 空間分布,不會有虛擬地址的概念,地址就是指在 RAM 中的物理地址。

為什麼需要虛擬內存?

下面都是我的理解,可能有不恰當的地方。

假設在裸機上運行一個程序,這時就沒辦法再同時執行其他程序了,需要引入操作系統來進行管理。有了操作系統之後,我們或許能運行多個不同程序,但很可能無法同時執行同一個程序的多個實例,因為同一個程序使用的物理地址是一樣的(假設是舊的編譯器),一起運行會有沖突。想運行同一個程序的多個實例,看起來有 2 種方案。

  1. 重新編譯一個使用其他地址的程序,和前一個不沖突。
  2. 運行該程序時使用添加地址偏移等方式保證使用地址不同。

第一個是讓編譯器來完成,理論上可以,但是會過於麻煩,如果我想同時運行3個,4個呢, 就必須多編譯幾次。
第二個呢,是讓操作系統來完成,應該算是虛擬內存的雛形了。使用該模式後,編譯器給出的程序內相關地址就不是實際物理地址了,算是虛擬地址。

X86 的虛擬內存技術

GDT/LDT

GDT 和 LDT 都是在 80286 的時候引入 x86 體系的,LDT 和 GDT 有著類似的結構。 LDT 的出現就是為了多進程使用獨立地址空間來服務的,通常每個進程一個 LDT, 而共享內存和內核內存則使用 GDT。 每個程序根據段描述符來確定基址,而且每個 Entry 裡面還有 limit 字段,正好可以對程序訪問空間作限制。但在 80386 引入了更優秀的分頁技術後,LDT 基本上就不再使用了。

分頁

分頁作為當前虛擬內存技術的實現,肯定有比 LDT 更好的地方,但它們的實現思路都是類似的。操作系統為每個進程維護一個 handle,這個handle關聯的是該進程從虛擬地址到物理地址轉換的相關數據塊。在 LDT 中 handle就是 LDT 指針與長度, 數據塊就是 LDT 自身。分頁模式下數據塊叫做 paging structure, handler 是指向其的指針。

paging structure 有 4096 bytes, 包含有獨立的 entries,不同模式下每個 entry 的大小不同。 每個 entry 包含一個物理地址,可以指向一個 page frame,也可以指向另一個 paging structur,也就是級聯的方式。 指向第一個 page structure 的指針在 CR3 寄存器裡, 之後從線性地址到物理地址的過程就是一個迭代的過程。線性地址的一部分用來指示對應的 entry, 該 entry 如果指向的是另一個 page structure 則繼續,直到指向了一個 page frame則表示地址轉換完成,使用最後這個 entry 作為基址,線性地址剩余部分表示偏移。

現在專門討論 32bit 模式下的分頁。32bit下每個 entry 4bytes,每個 paging structure包含 1024 個 entries, 需要 10bit 來區分每個 entry。實際上 32bit 模式下使用了 2 級 pageing structure。 第一級稱為 Page Directory, 使用 32bit 線性地址的 bits 31-22 來區分, 第二級稱為 Page Table, 使用 32bit 線性地址的 bits 21-12 來區分,剩下的 bits 11-0正好是用來計算在 4K page 裡的偏移。

在所有的線性地址到物理地址翻譯中, CR3,PDPTE,PDT, PTE等存儲的都是下一步的基址, 線性地址中存的則是偏移。

Copyright © Linux教程網 All Rights Reserved