在早期的計算機中,程式是直接執行在實體記憶體上的,也就是說:程式在執行時訪問的地址就是實體地址。這樣也就是單執行的時候沒有什麼問題!可是,計算機會有多到程式、分時系統和多工,當我們能夠同時執行多個程式時,
CPU
的利用率將會比較高。那麼有一個非常嚴重的問題:如何將計算機的有限的實體記憶體分配給多個程式使用
假設我們計算有128MB
記憶體,程式A需要10MB
,程式B需要100MB
,程式C需要20MB
。如果我們需要同時執行程式A和B,那麼比較直接的做法是將記憶體的前10MB
分配給程式A,10MB~110MB
分配給B。
但這樣做,會造成以下問題:
-
當多個程式需要執行時,必須保證這些程式用到的記憶體總量要小於計算機實際的實體記憶體的大小。
-
程式地址空間不隔離,由於程式是直接訪問實體記憶體的,所以每一個程式都可以修改其他程式的記憶體資料,設定修改核心地址空間中的資料,所以有些惡意程式可以隨意修改別的程式,就會造成一些破壞
-
記憶體使用效率低 記憶體空間不足,就需要將其他程式展示拷貝到硬碟當中,然後將新的程式裝入記憶體。然而由於大量的資料裝入裝出,記憶體的使用效率會非常低
-
程式執行的地址不確定;因為記憶體地址是隨機分配的,所以程式執行的地址也是不正確的
解決這幾個問題的思路就是使用我們非常牛逼的方法:增加中間層 - 即使用一種間接的地址訪問方式。
把程式給出的地址看做是一種虛擬地址,然後通過某種對映,將這個虛擬地址轉化到實際的實體地址。這樣,只需要控制好對映過程,就能保證程式所能訪問的實體記憶體區域跟別的程式不重疊,達到空間隔離的效果。
隔離
普通的程式它只需要一個簡單的執行環境
,一個單一的地址空間
,有自己的CPU
。
地址空間比較抽象,如果把它想象成一個陣列,每一個陣列是一位元組,陣列大小就是地址空間的長度,那麼32位的地址空間大小就是2^32=4294967296
位元組,即4G
,地址空間有效位是0x00000000~0xFFFFFFFF
。
地址空間分為兩種:
-
物理空間:就是實體記憶體。32位的機器,地址線就有32條,物理空間4G,但如果只裝有
512M
的記憶體,那麼實際有效的空間地址就是0x00000000~0x1FFFFFFF
,其他部分都是無效的。 -
虛擬空間:每個程式都有自己獨立的虛擬空間,而且每個程式只能訪問自己的空間地址,這樣就有效的做到了程式隔離。
分段
基本思路: 把一段與程式所需要的記憶體空間大小的虛擬空間對映到某個地址空間。虛擬空間的每個位元組對應物理空間的每個位元組。這個對映過程由軟體來完成。
比如A需要10M
,就假設有0x00000000
到0x00A00000
大小的虛擬空間,然後從實體記憶體分配一個相同大小的空間,比如是0x00100000
到0x00B00000
。作業系統來設定這個對映函式,實際的地址轉換由硬體完成。如果越界,硬體就會判斷這是一個非法訪問,拒絕這個地址請求,並上報作業系統或監控程式。
這樣一來利用:分段的方式可以解決之前的個(地址空間不隔離)和第三個問題(程式執行地址不確定)
-
首先做到了地址隔離,因為A和B被對映到了兩塊不同的物理空間,它們之間沒有任何重疊,如果A訪問虛擬空間的地址超過了
0x00A00000
這個範圍,硬體就會判斷這是一個非法的訪問,並將這個請求報告給作業系統或者監控程式,由它決定如何處理。 -
再者,對於每個程式來說,無論它們被分配到地址空間的哪一個區域,對於程式來說都是透明的,它們不需要關心實體地址的變化,它們只要按照從地址
0x00000000
到0x00A00000
來編寫程式、放置變數,所以程式不需要重定位。
第二問題記憶體使用效率問題依舊沒有解決。
但是分段的方法沒有解決記憶體使用效率的問題。分段對於記憶體區域的對映還是按照程式為單位,如果記憶體不足,被換入換出的磁碟的都是整個程式,這樣勢必會造成大量的磁碟訪問操作,從而嚴重影響速度,這種方法還是顯得粗糙,粒度比較大。事實上根據程式的區域性性原理,當一個程式正在執行時,在某個時間段內,它只是頻繁用到了一小部分資料,也就是說,程式的很多資料其實在一個時間段內是不會被用到的。人們很自然地想到了更小粒度的記憶體分割和對映方法,使得程式的區域性性原理得到充分利用,大大提高了記憶體的使用率。這種方法就是分頁。
分頁
分頁的基本方法是把地址空間人為得等分成固定大小的頁,每一個頁的大小由硬體決定,或硬體支援多種頁的大小,由作業系統選擇決定頁的大小。 目前幾乎所有PC的作業系統都是用4KB
大小的頁。我們使用的PC機是32位虛擬地址空間,也就是4GB
,按4KB
分頁,總共有1048576
個頁。
那麼,當我們把程式的虛擬地址空間按頁分割,把常用的資料和程式碼裝載到記憶體中,把不常用的程式碼和資料儲存在磁碟裡,當需要用到的時候再把它們從磁碟裡取出即可。圖中的線表示對映關係,我們可以看到虛擬空間有些頁被對映到同一個物理頁,這樣就可以實現記憶體共享。 虛擬頁,物理頁,磁碟頁根據記憶體空間不一樣而區分
我們可以看到Process 1 的VP2和VP3不在記憶體中,但是當程式需要用到這兩個頁的時候,硬體就會捕獲到這個訊息,就是所謂的頁錯誤(Page Fault),然後作業系統接管程式,負責將VP2和VP3從磁碟讀取出來裝入記憶體,然都將記憶體中的這兩個頁和VP2和VP3建立對映關係。以頁為單位存取和交換資料非常方便,硬體本身就支援這種以頁為單位的操作方式。
-
保護頁也是頁對映的目的之一,簡單地說就是每個頁可以設定許可權屬性,誰可以修改,誰可以訪問,而且只有作業系統有權修改這些屬性,那麼作業系統就可以做到保護自己和保護程式。
-
虛擬儲存的實現需要硬體支援,幾乎所有CPU都採用稱為MMU的部件來進行頁的對映:
在頁對映模式下,CPU
發出的是Virtual Address
,即我們程式看到的是虛擬地址
。經過MMU
轉換以後就變成了Physical Address
。一般MMU
整合在CPU
內部,不會以獨立的部件存在。
這篇文章是底層編譯連結的開門紅,這裡非常感謝《程式設計師的自我修養-連結、裝載與庫》這本書的大力分享,強烈推薦學習