歷史
在早期的計算機(jī)棚壁,程序是直接運行在物理內(nèi)存上的,也就是說继榆,程序所訪問的地址都是物理內(nèi)存巾表。但是我們往往需要同時運行多個程序,所以就有一個很明顯的問題如何把計算機(jī)上有限的物理內(nèi)存分配給多個程序使用略吨?
舉個例子
假如我們計算機(jī)有128M內(nèi)存集币,現(xiàn)在:
- 程序A需要10M內(nèi)存
- 程序B需要100M內(nèi)存
- 程序C需要20M內(nèi)存
如果我們需要同時運行A和B,那么比較直接的做法是這樣的翠忠,0-10M分給A鞠苟,10-110M分給B,如下:

這樣分配秽之,問題會有很多当娱,大致有以下三個:
-
地址空間不隔離: 惡意程序可以很容易改寫其他程序的內(nèi)存數(shù)據(jù),如A可以通過類似
*(p + 1024 * 1024 * 10) = xxx
來訪問到B里的內(nèi)存政溃,這是很危險的趾访。 - 內(nèi)存使用率低: 比如我們在A、B同時運行的時候董虱,突然要運行C扼鞋。這個時候內(nèi)存已經(jīng)不夠,我們可以采取的一個辦法是把某個程序的數(shù)據(jù)暫時寫到磁盤里愤诱,等到需要的時候再讀回來云头。這個時候問題來了,由于地址空間是連續(xù)的淫半,我們?nèi)〕鯝也沒法把C放入內(nèi)存運行溃槐,只能取出B,可以看到這是一個巨大的IO操作科吭,會導(dǎo)致效率十分低下昏滴。
- 程序運行的地址不確定:因為程序每次需要裝入運行時我們都需要一塊足夠大的空閑區(qū)域猴鲫,這個區(qū)域是不確定的,就需要程序重定位谣殊。
怎么辦拂共?還是那個熟悉的配方,熟悉的味道:增加中間層姻几。
一切計算機(jī)的問題都可以通過增加中間層來解決宜狐。
思想:我們把程序的地址視為一種虛擬地址,然后通過映射的方式將這個虛擬地址轉(zhuǎn)換成實際的物理地址蛇捌。只要我們妥善的處理映射的方式抚恒,就能達(dá)到空間隔離的效果。
分段管理
這個時候分段的管理方法就出現(xiàn)了络拌,先上圖:

分段管理:把一段程序所需要的內(nèi)存大小的虛擬空間映射到某個地址空間俭驮。
零散論點:分段的虛擬空間大小不一定相等,這是一個和分頁不同的特點盒音。
如上圖表鳍,我們A需要10M的大小,那么我們的虛擬空間就是從0x00000000開始到0x00A00000結(jié)束祥诽。所以:
- 如果A訪問的地址空間超過了0x00A00000譬圣,那么硬件就會判斷這是個非法訪問,就會拒絕這個請求雄坪,從而實現(xiàn)了隔離厘熟。
- 對于程序來說,我們只在0x00000000到0x00A00000來編寫程序维哈、放置變量绳姨,所以程序不需要再重定位。
分段解決了但是分段還是沒有解決我們第二個問題-內(nèi)存使用效率問題阔挠。人們很自然的就想到了更小粒度的內(nèi)存分割和映射方法飘庄,于是,分頁出現(xiàn)了购撼。
分頁管理
分頁的基本方法是把地址空間人為的分成固定大小的頁跪削,一般都是4KB。
首先我們要知道三種頁:
- 磁盤頁:磁盤中的頁迂求。
- 虛擬頁:虛擬空間的頁碾盐。
- 物理頁:物理內(nèi)存中的頁。
程序會放在虛擬頁中揩局,最后進(jìn)行映射毫玖。

我們把程序中常用的數(shù)據(jù)和代碼頁裝到內(nèi)存中(虛擬頁中0、1、7)付枫,把不常用的放在磁盤頁(虛擬頁中2烹玉、3),暫未用到的不映射(虛擬頁中的4励背、5春霍、6)。
頁錯誤
如果進(jìn)程需要使用虛擬頁的2叶眉、3,但是這時它們在磁盤頁中芹枷,硬件會捕獲這個消息衅疙,這就是所謂的頁錯誤。
這時操作系統(tǒng)會用自己的辦法(用空再寫鸳慈,就是TLB那塊)把磁盤中表示虛擬頁2饱溢、3的數(shù)據(jù)讀出來并且裝入內(nèi)存。
以頁為單位來存取和交換這些數(shù)據(jù)非常方便走芋,硬件本身就支持這種以頁為單位的操作方式绩郎。
MMU
映射需要硬件來實現(xiàn),幾乎所有的硬件都采用一個叫MMU的部件來進(jìn)行映射翁逞,如圖所示肋杖。

寫在結(jié)尾
如果您有所收獲,希望點個贊支持挖函。
也歡迎收錄我的博客專欄:biggergao的博客状植,謝謝。
Done
作者 @biggergao
2016年06月04日