在早期的計算機中,程序是直接運行在物理內存上的,也就是說:程序在運行時訪問的地址就是物理地址。這樣也就是單運行的時候沒有什麼問題!可是,計算機會有多到程序、分時系統和多任務,當我們能夠同時運行多個程序時,CPU的利用率將會比較高。那麼有一個非常嚴重的問題:如何將計算機的有限的物理內存分配給多個程序使用
假設我們計算有128MB內存,程序A需要10MB,程序B需要100MB,程序C需要20MB。如果我們需要同時運行程序A和B,那麼比較直接的做法是將內存的前10MB分配給程序A,10MB~110MB分配給B。
但這樣做,會造成以下問題:
解決這幾個問題的思路就是使用我們非常牛逼的方法:增加中間層 - 即使用一種間接的地址訪問方式。
把程序給出的地址看做是一種虛擬地址,然後通過某種映射,將這個虛擬地址轉化到實際的物理地址。這樣,只需要控制好映射過程,就能保證程序所能訪問的物理內存區域跟別的程序不重疊,達到空間隔離的效果。
隔離
普通的程序它只需要一個簡單的執行環境,一個單一的地址空間,有自己的CPU。
地址空間比較抽象,如果把它想像成一個數組,每一個數組是一位元組,數組大小就是地址空間的長度,那麼32位的地址空間大小就是2^32=4294967296位元組,即4G,地址空間有效位是0x00000000~0xFFFFFFFF。
地址空間分為兩種:
分段
基本思路: 把一段與程序所需要的內存空間大小的虛擬空間映射到某個地址空間。虛擬空間的每個位元組對應物理空間的每個位元組。這個映射過程由軟體來完成。
比如A需要10M,就假設有0x00000000 到0x00A00000大小的虛擬空間,然後從物理內存分配一個相同大小的空間,比如是0x00100000到0x00B00000。作業系統來設置這個映射函數,實際的地址轉換由硬體完成。如果越界,硬體就會判斷這是一個非法訪問,拒絕這個地址請求,並上報作業系統或監控程序。
這樣一來利用分段的方式可以解決之前的地址空間不隔離和程序運行地址不確定
第二問題內存使用效率問題依舊沒有解決。
但是分段的方法沒有解決內存使用效率的問題。分段對於內存區域的映射還是按照程序為單位,如果內存不足,被換入換出的磁碟的都是整個程序,這樣勢必會造成大量的磁碟訪問操作,從而嚴重影響速度,這種方法還是顯得粗糙,粒度比較大。事實上根據程序的局部性原理,當一個程序正在運行時,在某個時間段內,它只是頻繁用到了一小部分數據,也就是說,程序的很多數據其實在一個時間段內是不會被用到的。人們很自然地想到了更小粒度的內存分割和映射方法,使得程序的局部性原理得到充分利用,大大提高了內存的使用率。這種方法就是分頁。
分頁
分頁的基本方法是把地址空間人為得等分成固定大小的頁,每一個頁的大小由硬體決定,或硬體支持多種頁的大小,由作業系統選擇決定頁的大小。 目前幾乎所有PC的作業系統都是用4KB大小的頁。我們使用的PC機是32位虛擬地址空間,也就是4GB,按4KB分頁,總共有1048576個頁。
那麼,當我們把進程的虛擬地址空間按頁分割,把常用的數據和代碼裝載到內存中,把不常用的代碼和數據保存在磁碟里,當需要用到的時候再把它們從磁碟里取出即可。圖中的線表示映射關係,我們可以看到虛擬空間有些頁被映射到同一個物理頁,這樣就可以實現內存共享。
虛擬頁,物理頁,磁碟頁根據內存空間不一樣而區分
我們可以看到Process 1 的VP2和VP3不在內存中,但是當進程需要用到這兩個頁的時候,硬體就會捕獲到這個消息,就是所謂的頁錯誤(Page Fault),然後作業系統接管進程,負責將VP2和VP3從磁碟讀取出來裝入內存,然都將內存中的這兩個頁和VP2和VP3建立映射關係。以頁為單位存取和交換數據非常方便,硬體本身就支持這種以頁為單位的操作方式。
在頁映射模式下,CPU發出的是Virtual Address,即我們程序看到的是虛擬地址。經過MMU轉換以後就變成了Physical Address。一般MMU集成在CPU內部,不會以獨立的部件存在。