天天看點

程序位址空間與虛拟存儲空間的了解

在進入正題前先來談談作業系統記憶體管理機制的發展曆程,了解這些有利于我們更好的了解目前作業系統的記憶體管理機制。

一早期的記憶體配置設定機制

在 早期的計算機中,要運作一個程式,會把這些程式全都裝入記憶體,程式都是直接運作在記憶體上的,也就是說程式中通路的記憶體位址都是實際的實體記憶體位址。當計算 機同時運作多個程式時,必須保證這些程式用到的記憶體總量要小于計算機實際實體記憶體的大小。那當程式同時運作多個程式時,作業系統是如何為這些程式配置設定記憶體 的呢?下面通過執行個體來說明當時的記憶體配置設定方法:

某台計算機總的記憶體大小是128M,現在同時運作兩個程式A和B,A需占用記憶體10M,B需占用記憶體110。計算機在給程式配置設定記憶體時會采取這樣的方法:先将記憶體中的前10M配置設定給程式A,接着再從記憶體中剩餘的118M中劃分出110M配置設定給程式B。這種配置設定方法可以保證程式A和程式B都能運作,但是這種簡單的記憶體配置設定政策問題很多。

程式位址空間與虛拟存儲空間的了解

圖一早期的記憶體配置設定方法

問題1:程序位址空間不隔離。由于程式都是直接通路實體記憶體,是以惡意程式可以随意修改别的程序的記憶體資料,以達到破壞的目的。有些非惡意的,但是有bug的程式也可能不小心修改了其它程式的記憶體資料,就會導緻其它程式的運作出現異常。這種情況對使用者來說是無法容忍的,因為使用者希望使用計算機的時候,其中一個任務失敗了,至少不能影響其它的任務。

問題2:記憶體使用效率低。在A和B都運作的情況下,如果使用者又運作了程式C,而程式C需要20M大小的記憶體才能運作,而此時系統隻剩下8M的空間可供使用,是以此時系統必須在已運作的程式中選擇一個将該程式的資料暫時拷貝到硬碟上,釋放出部分空間來供程式C使用,然後再将程式C的資料全部裝入記憶體中運作。可以想象得到,在這個過程中,有大量的資料在裝入裝出,導緻效率十分低下。

問題3:程式運作的位址不确定。當記憶體中的剩餘空間可以滿足程式C的要求後,作業系統會在剩餘空間中随機配置設定一段連續的20M大小的空間給程式C使用,因為是随機配置設定的,是以程式運作的位址是不确定的。

二分段

為 了解決上述問題,人們想到了一種變通的方法,就是增加一個中間層,利用一種間接的位址通路方法通路實體記憶體。按照這種方法,程式中通路的記憶體位址不再是實 際的實體記憶體位址,而是一個虛拟位址,然後由作業系統将這個虛拟位址映射到适當的實體記憶體位址上。這樣,隻要作業系統處理好虛拟位址到實體記憶體位址的映 射,就可以保證不同的程式最終通路的記憶體位址位于不同的區域,彼此沒有重疊,就可以達到記憶體位址空間隔離的效果。

當建立一個程序時,作業系統會為該程序配置設定一個4GB大小的虛拟 程序位址空間。之是以是4GB,是因為在32位的作業系統中,一個指針長度是4位元組,而4位元組指針的尋址能力是從0x00000000~0xFFFFFFFF,最大值0xFFFFFFFF表示的即為4GB大小的容量。與虛拟位址空間相對的,還有一個實體位址空間,這個位址空間對應的是真實的實體記憶體。如果你的計算機上安裝了512M大小的記憶體,那麼這個實體位址空間表示的範圍是0x00000000~0x1FFFFFFF。當作業系統做虛拟位址到實體位址映射時,隻能映射到這一範圍,作業系統也隻會映射到這一範圍。當程序建立時,每個程序都會有一個自己的4GB虛拟位址空間。要注意的是這個4GB的位址空間是“虛拟”的,并不是真實存在的,而且每個程序隻能通路自己虛拟位址空間中的資料,無法通路别的程序中的資料,通過這種方法實作了程序間的位址隔離。那是不是這4GB的虛拟位址空間應用程式可以随意使用呢?很遺憾,在Windows系統下,這個虛拟位址空間被分成了4部分:NULL指針區、使用者區、64KB禁入區、核心區。應用程式能使用的隻是使用者區而已,大約2GB左右(最大可以調整到3GB)。核心區為2GB,核心區儲存的是系統線程排程、記憶體管理、裝置驅動等資料,這部分資料供所有的程序共享,但應用程式是不能直接通路的。

人 們之是以要建立一個虛拟位址空間,目的是為了解決程序位址空間隔離的問題。但程式要想執行,必須運作在真實的記憶體上,是以,必須在虛拟位址與實體位址間建 立一種映射關系。這樣,通過映射機制,當程式通路虛拟位址空間上的某個位址值時,就相當于通路了實體位址空間中的另一個值。人們想到了一種分段(Sagmentation)的方法,它的思想是在虛拟位址空間和實體位址空間之間做一一映射。比如說虛拟位址空間中某個10M大小的空間映射到實體位址空間中某個10M大小的空間。這種思想了解起來并不難,作業系統保證不同程序的位址空間被映射到實體位址空間中不同的區域上,這樣每個程序最終通路到的

實體位址空間都是彼此分開的。通過這種方式,就實作了程序間的位址隔離。還是以執行個體說明,假設有兩個程序A和B,程序A所需記憶體大小為10M,其虛拟位址空間分布在0x00000000到0x00A00000,程序B所需記憶體為100M,其虛拟位址空間分布為0x00000000到0x06400000。那麼按照分段的映射方法,程序A在實體記憶體上映射區域為0x00100000到0x00B00000,,程序B在實體記憶體上映射區域為0x00C00000到0x07000000。于是程序A和程序B分别被映射到了不同的記憶體區間,彼此互不重疊,實作了位址隔離。從應用程式的角度看來,程序A的位址空間就是分布在0x00000000到0x00A00000,在做開發時,開發人員隻需通路這段區間上的位址即可。應用程式并不關心程序A究竟被映射到實體記憶體的那塊區域上了,是以程式的運作位址也就是相當于說是确定的了。圖二顯示的是分段方式

的記憶體映射方法。

程式位址空間與虛拟存儲空間的了解

圖二分段方式的記憶體映射方法

這 種分段的映射方法雖然解決了上述中的問題一和問題三,但并沒能解決問題二,即記憶體的使用效率問題。在分段的映射方法中,每次換入換出記憶體的都是整個程式, 這樣會造成大量的磁盤通路操作,導緻效率低下。是以這種映射方法還是稍顯粗糙,粒度比較大。實際上,程式的運作有局部性特點,在某個時間段内,程式隻是訪 問程式的一小部分資料,也就是說,程式的大部分資料在一個時間段内都不會被用到。基于這種情況,人們想到了粒度更小的記憶體分割和映射方法,這種方法就是分頁(Paging)。

三分頁

分頁的基本方法是,将位址空間分成許多的頁。每頁的大小由CPU決定,然後由作業系統選擇頁的大小。目前Inter系列的CPU支援4KB或4MB的頁大小,而PC上目前都選擇使用4KB。按這種選擇,4GB虛拟位址空間共可以分成1048576個頁,512M的實體記憶體可以分為131072個頁。顯然虛拟空間的頁數要比實體空間的頁數多得多。

在 分段的方法中,每次程式運作時總是把程式全部裝入記憶體,而分頁的方法則有所不同。分頁的思想是程式運作時用到哪頁就為哪頁配置設定記憶體,沒用到的頁暫時保留在 硬碟上。當用到這些頁時再在實體位址空間中為這些頁配置設定記憶體,然後建立虛拟位址空間中的頁和剛配置設定的實體記憶體頁間的映射。

下面通過介紹一個可執行檔案的裝載過程來說明分頁機制的實作方法。一個可執行檔案(PE檔案)其實就是一些編譯連結好的資料和指令的集合,它也會被分成很多頁,在PE檔案執行的過程中,它往記憶體中裝載的機關就是頁。當一個PE檔案被執行時,作業系統會先為該程式建立一個4GB的程序虛拟位址空間。前面介紹過,虛拟位址空間隻是一個中間層而已,它的功能是利用一種映射機制将虛拟位址空間映射到實體位址空間,是以,建立4GB虛拟位址空間其實并不是要真的建立空間,隻是要建立那種映射機制所需要的資料結構而已,這種資料結構就是頁目和頁表。

當建立完虛拟位址空間所需要的資料結構後,程序開始讀取PE檔案的第一頁。在PE檔案的第一頁包含了PE檔案頭和段表等資訊,程序根據檔案頭和段表等資訊,将PE檔案中所有的段一一映射到虛拟位址空間中相應的頁(PE檔案中的段的長度都是頁長的整數倍)。這時PE檔案的真正指令和資料還沒有被裝入記憶體中,作業系統隻是根據PE檔案的頭部等資訊建立了PE檔案和程序虛拟位址空間中頁的映射關系而已。當CPU要通路程式中用到的某個虛拟位址時,當CPU發現該位址并沒有相相關聯的實體位址時,CPU認為該虛拟位址所在的頁面是個空頁面,CPU會認為這是個頁錯誤(Page Fault),CPU也就知道了作業系統還未給該PE頁面配置設定記憶體,CPU會将控制權交還給作業系統。作業系統于是為該PE頁面在實體空間中配置設定一個頁面,然後再将這個實體頁面與虛拟空間中的虛拟頁面映射起來,然後将控制權再還給程序,程序從剛才發生頁錯誤的位置重新開始執行。由于此時已為PE檔案的那個頁面配置設定了記憶體,是以就不會發生頁錯誤了。随着程式的執行,頁錯誤會不斷地産生,作業系統也會為程序配置設定相應的實體頁面來滿足程序執行的需求。

分頁方法的核心思想就是當可執行檔案執行到第x頁時,就為第x頁配置設定一個記憶體頁y,然後再将這個記憶體頁添加到程序虛拟位址空間的映射表中,這個映射表就相當于一個y=f(x)函數。應用程式通過這個映射表就可以通路到x頁關聯的y頁了。

繼續閱讀