虚拟内存相关原理
內存管理
- 1.虛擬內存
- 2.內存分段
- 3.內存分頁
- 多級頁表
- TLB
1.虛擬內存
眾所周知:單片機是沒有操作系統的,所以單片機的CPU是直接操作內存的物理地址(physical address,PA)
在這種情況下,程序內存中是無法同時運行多個程序。例如第一個程序在10位置的位置存放立一個數值,當運行第二個程序時,會擦掉第一個程序在10位置上存儲的數值。因此,單片機是無法同時運行多個程序的。
但我們用的電腦幾乎都可以同時運行多個個程序,這是如何實現的呢。
由單片機的示例可知,兩個程序都使用了絕對物理地址,那么這就是我們需要解決的問題。
我們可以使用虛擬尋址的方法解決,即讓操作系統為每個進程分配獨立的一套虛擬地址,每個程序都有,互不干涉。但每個進程都不能直接操作物理內存地址。那么虛擬地址如何與物理地址相對應呢,這就需要操作系統出手了
操作系統會提供一種機制,將不同進程的虛擬地址和不同內存的物理地址映射起來
如上圖,程序訪問虛擬地址的時候,由操作系統將其轉換為不同的物理地址,這樣不同的程序運行時,寫入的物理地址是不同的,這樣就不會產生沖突了。
這就引出了兩種地址的概念
- 程序使用的地址叫做虛擬內存地址(virtual memory address)
- 實際存在硬件里面的空間地址叫物理內存地址(physical memory address)
操作系統引入虛擬內存,進程持有的虛擬地址會通過CPU芯片中的內存管理單元(MMU)1 的映射關系
那么操作系統是如何管理虛擬地址與物理地址之間的關系呢?
- 內存分段
- 內存分頁
內存分段的提出比較早,所以我們先看看內存分段。
2.內存分段
程序是由若干邏輯分段組成的,如可有代碼分段,數據分段,棧段,堆段組成。不同的段是有不同的屬性的,所以就用分段(segmentation)的形式把這些段分離出來。
分段機制下,虛擬地址和物理地址是如何映射的?分段機制下的虛擬地址由兩部分組成,段選擇子 和 段內偏移量。
- 段選擇子就保存在段寄存器里面。段選擇子里面最重要的是段號,用作段表的索引。段表里面保存的是這個段的基地址、段的界限和特權等級等。
- 虛擬地址中的段內偏移量應該位于 0 和段界限之間,如果段內偏移量是合法的,就將段的基地址加上段偏移量,這樣就得到了該段全部的地址范圍。
我們知道了虛擬地址是通過段表與物理地址進行映射的,分段機制會將程序的虛擬地址分成4個段,每個段在段表中有一個項,在這一項中找到段的基地址再加上段內偏移量,這樣就得到段的物理地址了。
假如我們要訪問段 2 中偏移量為3000 的虛擬地址,我們可以計算出物理地址為 3000+3000 = 6000 ,那么物理地址 3000~6000 就是段 3 對應的物理地址范圍。
分段的方法雖然很好,但是也有兩個缺點:
- 第一個就是內存碎片的問題
- 第二個就是內存交換效率低的問題
我們接下來討論為什么會有這兩個問題。
產生內存碎片的的原因
我們來看個例子。假設有 1G 的物理內存,用戶執行了多個程序,其中
- 游戲占了 512 MB 內存
- 瀏覽器占了 128 MB 內存
- 音樂占了 256 MB 內存
這個時候如果我們關閉瀏覽器,則空閑內存還有 1024 - 512 - 256 = 256 MB。
如果這個 256 MB 內存不是連續的 被分成兩個128 MB 內存,這樣就會導致無法打開一個 200 MB 內存的程序。
這里的內存碎片主要有兩部分
- 外部內存碎片,也就是產生了多個不連續的小物理內存,導致新的程序無法被裝載;
- 內部內存碎片,程序所有的內存都被裝載到了物理內存,但是這個程序有部分的內存可能并不是很常使用,這也會導致內存的浪費
針對以上兩種內存碎片,解決方式有所不同。
解決外部內存碎片的方式j就是內存交換
我們可以把音樂程序占用256 MB 內存寫到硬盤上,然后再從硬盤上加載到內存中,但我們不能加載回原來的位置,而是加載到 512 MB 內存的后面,這樣就可以空出 258 MB 內存 ,這樣就滿足了 200 MB 內存的要求。
在Linux系統中,這個內存交換空間就是我們常(其實不常)看到的swap 空間,這塊空間是從硬盤中劃分出來的,專門用于內存交換。
但是這就引出了第二個問題:內存交換效率低
因為硬盤的訪問速度要比內存慢多了
3.內存分頁
分段的好處就是能夠產生連續的內存空間,但是會出現內存碎片和內存交換速率低的問題
要解決這些問題,那么就要想出能少出現一些內存碎片的方法。另外當需要進行內存交換是讓需要交換寫入或從磁盤裝載的數據更少一點,這樣就解決問題了 。這個辦法就是內存分頁
內存分頁是吧整個虛擬和物理內存空間切成一段段固定尺寸的大小
這樣一個連續并且尺寸固定的內存空間,我們叫頁。在Linux下,每一頁的大小為 4 KB。
虛擬地址與物理地址之間通過頁表2進行映射,如下圖
頁表實際上存儲在CPU的內存管理單元(MMU)
中,于是CPU可以通過MMU,找出實際要訪問的物理內存地址。
而當進程訪問的虛擬內存空間在頁表中查詢不到時,系統會產生一個缺頁異常,j進入系統內核空間分配物理內存、更新進程頁表,最后再返回用戶空間,恢復進程運行。
分頁是怎么解決分段的內存碎片、內存交換效率低的問題?
由于內存空間都是預先劃分好的,也就不會像分段會產生間隙非常小的內存,這正是分段會產生內存碎片的原因。**而采用分頁那么釋放內存都是以頁為單位釋放的,就不會產生無法給進程使用的小內存。
如果內存不夠用了,那么操作系統會將其他正在運行的進程的【最近沒有使用內存頁面】給釋放掉,也就是暫時寫在硬盤上,成為換出空間(swap up)。一旦需要的時候,在加載進來,稱為換入。所以,一次性寫入磁盤的也只有少數的一個頁或者幾個頁,不會花太多時間,內存交換的效率就此相對比較高
進一步講,分頁的方式使得我們在加載程序的時候,不需要一次性都把程序加載到物理內存中。我們完全可以在虛擬內存的頁和物理內存的頁進行映射后,并不需要真的把頁加載到物理內存中,而只是在需要的時候將需要的虛擬內存中的數據或指令加載到物理內存中
分頁機制下,虛擬地址和物理地址是如何映射的?
在分頁的機制下,虛擬地址分為兩部分,頁號和頁內偏移。頁號作為頁表的索引,頁表包含虛擬內存頁每頁所在的物理內存的基地址,這個基地址與頁內偏移的組合就形成了物理內存的地址,見下圖
總結一下,對于每個內存地址的轉換,有三個步驟:
- 虛擬內存地址,切分成頁號和偏移量;
- 根據頁號,從頁表里面,查詢對應的物理頁號;
- 直接拿到物理頁號,加上前面的偏移量,就得到了物理內存地址。
但是這種分頁在實際操作中會有缺陷的。
簡單的分頁有什么缺陷呢
有空間上的缺陷
當操作系統運行非常多的的進程時,那就意味著頁表會非常的大。
至于為什么會非常大呢,我就不寫了,嘿嘿嘿
這時就需要一個更高端的方式來解決簡單的分頁帶來的問題了
多級頁表
要解決上面的問題,就需要采取多級頁表的解決方案
假設有100多萬個【頁表項】的單級頁表再分頁,將頁表(一級頁表)分為1024個頁表(二級頁表),每個表(二級頁表)中包含1024個頁表項,形成二級分頁
如下圖所示:
這是你就有可能問了,整這么·多的表那所占的空間不就更大了嗎
其實不是這樣的,我們應該換一個角度去看問題,記得計算機組成原理里面無處不在的局部性原理么?
每個進程都有 4GB 的虛擬地址空間,而顯然對于大多數程序來說,其使用到的空間遠未達到 4GB,因為會存在部分對應的頁表項都是空的,根本沒有分配,對于已分配的頁表項,如果存在最近一定時間未訪問的頁表,在物理內存緊張的情況下,操作系統會將頁面換出到硬盤,也就是說不會占用物理內存。
如果使用了二級分頁,一級頁表就可以覆蓋整個 4GB 虛擬地址空間,但如果某個一級頁表的頁表項沒有被用到,也就不需要創建這個頁表項對應的二級頁表了,即可以在需要時才創建二級頁表。
那么為什么不分級的頁表就做不到這樣節約內存呢?我們從頁表的性質來看,保存在內存中的頁表承擔的職責是將虛擬地址翻譯成物理地址。假如虛擬地址在頁表中找不到對應的頁表項,計算機系統就不能工作了。所以頁表一定要覆蓋全部虛擬地址空間,不分級的頁表就需要有 100 多萬個頁表項來映射,而二級分頁則只需要 1024 個頁表項(此時一級頁表覆蓋到了全部虛擬地址空間,二級頁表在需要時創建)。
我們把二級分頁再推廣到多級頁表,就會發現頁表占用的內存空間更少了,這一切都要歸功于對局部性原理的充分應用。
TLB
多級頁表雖然解決了空間上的問題,但是虛擬地址到物理地址的轉換就多了幾道工序,那么所花費的時間就更長了
程序是有局限性的,即在某一段時間內,整個程序的執行僅限于程序中的某一部分。相應的,執行所訪問的存儲空間頁局限于某個內存區域。
我們就可以利用這一特性,把最常訪問的幾個表項存儲到訪問速度更快的硬件,于是計算機科學家們,就在 CPU 芯片中,加入了一個專門存放程序最常訪問的頁表項的 Cache,這個Cache 就是 TLB (Translation lookaside buffer),通常稱為頁表緩存,轉址旁路緩存,快表等。
在cpu 芯片里面,封裝了內存管理的單元芯片,它用來完成地址轉換和與TLB的交互。
在CPU 尋址時,會先查找TLB ,如果沒找到,在查找常規表。
CPU上的芯片叫做內存管理單元(memory management unit,MMU) 的專用硬件,利用存放在主存中的查詢表來動態翻譯虛擬地址,該表的內存由操作系統管理 ??
頁表將虛擬頁映射到物理頁。每次地址翻譯硬件將一個虛擬地址裝轉換為物理地址時,都會讀取頁表。操作系統負責維護頁表的內容,以及在磁盤與DRAM之間來回傳頁 ??
總結
- 上一篇: 计算机系统基础:设备管理采用的相关技术知
- 下一篇: 文本纠错