天天看點

作業系統記憶體配置設定(隔離,分段和分頁)

記憶體配置設定問題

在早期的計算機中,程式是直接運作在實體記憶體上的,也就是說,程式在運作時所通路的位址都是實體位址。如果一個計算機同時隻運作一個程式,那麼隻要程式要求的記憶體空間不要超過實體記憶體的大小,就不會有問題。但為了更有效地利用硬體資源,我們必須同時運作多個程式,正如之前提到的多道程式、分時系統和多任務中一樣,當我們能夠同時運作多個程式時,CPU 的使用率将會提高。那麼關鍵的問題是,

如何将計算機上有限的實體記憶體配置設定給多個程式使用

假設我們的計算機有128MB記憶體,程式A運作需要10MB,程式B需要100MB,程式C需要20MB。如果我們需要同時運作程式A和B,那麼比較直接的做法是将記憶體的前10MB配置設定給程式A,10MB~110MB配置設定給B。這樣就能夠實作A和B兩個程式同時運作,但是這種簡單的記憶體配置設定政策問題很多。

1.位址空間不隔離: 所有程式都直接通路實體位址,程式所使用的記憶體空間不是互相隔離的。惡意的程式可以很容易改寫其他程式的記憶體資料,以達到破壞的目的;有些非惡意的、但是有臭蟲的程式可能不小心修改了其他程式的資料,就會使其他程式也崩潰,這對于需要安全穩定的計算環境的使用者來說是不能容忍的。使用者希望他在使用計算機的時候,其中一個任務失敗了,至少不會影響其他任務。

2.記憶體使用效率低: 由于沒有有效的記憶體管理機制,通常需要一個程式執行時,監控程式就将整個程式裝入記憶體中然後開始執行。如果我們忽然需要運作程式C,那麼這時記憶體空間其實已經不夠了,這時候我們可以用的一個辦法是将其他程式的資料暫時寫到磁盤裡面,等到需要用到的時候再讀回來。由于程式所需要的空間是連續的,那麼這個例子裡面,如果我們将程式A換出到磁盤所釋放的記憶體空間是不夠的,是以隻能将B換出到磁盤,然後将C讀入到記憶體開始運作。可以看到整個過程中有大量的資料在換入換出,導緻效率十分低下。

3.程式運作的位址不确定: 因為程式每次需要裝入運作時,我們都需要給它從記憶體中配置設定一塊足夠大的空閑區域,這個空閑區域的位置是不确定的。這給程式的編寫造成了一定的麻煩,因為程式在編寫時,它通路資料和指令跳轉時的目标位址很多都是固定的,這涉及程式的重定位問題。

關于隔離

使用者程式在運作時不希望介入到這些複雜的存儲器管理過程中,作為普通的程式,它需要的是一個簡單的執行環境,有一個單一的位址空間、有自己的CPU,好像整個程式占有整個計算機而不用關心其他的程式(當然程式間通信的部分除外,因為這是程式主動要求跟其他程式通信和聯系)。所謂的位址空間是個比較抽象的概念,你可以把它想象成一個很大的數組,每個數組的元素是一個位元組,而這個數組大小由位址空間的位址長度決定,比如32位的位址空間的大小為232=4294967296位元組,即4GB,位址空間有效的位址是0~4294967295,用十六進制表示就是0x00000000~0xFFFFFFFF。位址空間分兩種:虛拟位址空間(Virtual Address Space)和實體位址空間(Physical Address Space)。實體位址空間是實實在在存在的,存在于計算機中,而且對于每一台計算機來說隻有唯一的一個,你可以把實體空間想象成實體記憶體,比如你的計算機用的是Intel的Pentium4的處理器,那麼它是32位的機器,即計算機位址線有32條(實際上是36條位址線,不過我們暫時認為它隻是32條),那麼實體空間就有4GB。但是你的計算機上隻裝了512MB的記憶體,那麼其實實體位址的真正有效部分隻有0x00000000~0x1FFFFFFF,其他部分都是無效的(實際上還有一些外部IO 裝置映射到實體空間的,也是有效的,但是我們暫時無視其存在)。虛拟位址空間是指虛拟的、人們想象出來的位址空間,其實它并不存在,每個程序都有自己獨立的虛拟空間,而且每個程序隻能通路白己的位址空間,這樣就有效地做到了程序的隔離。

在作業系統中,程序與程序間的記憶體和資料都是不共享的。兩個程序就好像大海中互相獨立的兩個島嶼,各自生活在互相平行的兩個世界中,互不幹擾,各自為政。這樣做的目的,是為了避免程序間互相操作資料的現象發生,進而引起各自的安全問題。為了實作程序隔離,采用了虛拟位址空間,兩個程序各自的虛拟位址不同,從邏輯上來實作彼此間的隔離。

分段(Segmentation)

最開始人們使用的是一種叫做分段(Segmentation) 的方法,

基本思路是把一段與程式所需要的記憶體空間大小的虛拟空間映射到某個位址空間

。比如程式A需要10MB記憶體,那麼我們假設有一個位址從0x00000000到0x00A00000的10MB大小的一個假象的空間,也就是虛拟空間,然後我們從實際的實體記憶體中配置設定一個相同大小的實體位址,假設是實體位址0x00100000開始到0x00B00000結束的一塊空間。然後我們把這兩塊相同大小的位址空間一一映射,即虛拟空間中的每個位元組相對應于實體空間中的每個位元組。這個映射過程由軟體來設定,比如作業系統來設定這個映射函數,實際的位址轉換由硬體完成。比如當程式A中通路位址0x00001000時,CPU會将這個位址轉換成實際的實體位址0x00101000。那麼比如程式A和程式B在運作時,它們的虛拟空間和實體空間映射關系可能如圖所示:

作業系統記憶體配置設定(隔離,分段和分頁)

分段的方法基本解決了上面提到的3個問題中的第一個和第三個。首先它做到了位址隔離,因為程式A和程式B被映射到了兩塊不同的實體空間區域,它們之間沒有任何重疊,如果程式A通路虛拟空間的位址超出了0x00A00000這個範圍,那麼硬體就會判斷這是一個非法的通路,拒絕這個位址請求,并将這個請求報告給作業系統或監控程式,由它來決定如何處理。再者,對于每個程式來說,無論它們被配置設定到實體位址的哪一個區域,對于程式來說都是透明的,它們不需要關心實體位址的變化,它們隻需要按照從位址0x00000000 到0x00A00000來編寫程式、放置變量,是以程式不再需要重定位。

但是分段的這種方法還是沒有解決我們的第二個問題,即記憶體使用效率的問題。分段對記憶體區域的映射還是按照程式為機關,如果記憶體不足,被換入換出到磁盤的都是整個程式,這樣勢必會造成大量的磁盤通路操作,進而嚴重影響速度,這種方法還是顯得粗糙,粒度比較大。事實上,根據程式的局部性原理,當一個程式在運作時,在某個時間段内,它隻是頻繁地用到了一小部分資料,也就是說,程式的很多資料其實在一個時間段内都是不會被用到的。人們很自然地想到了更小粒度的記憶體分割和映射的方法,使得程式的局部性原理得到充分的利用,大大提高了記憶體的使用率。這種方法就是分頁(Paging)。

分頁(Paging)

分頁的基本方法是把位址空間人為地等分成固定大小的頁,每一頁的大小由硬體決定,或硬體支援多種大小的頁,由作業系統選擇決定頁的大小

。比如Intel Pentium系列處理器支援4KB或4MB的頁大小,那麼作業系統可以選擇每頁大小為4KB,也可以選擇每頁大小為4MB,但是在同一時刻隻能選擇一種大小,是以對整個系統來說,頁就是固定大小的。目前幾乎所有的PC上的作業系統都使用4KB大小的頁。我們使用的PC機是32位的虛拟位址空間,也就是4GB,那麼按4KB每頁分的話,總共有1048576個頁。實體空間也是同樣的分法。

下面我們來看一個簡單的例子,如圖所示,每個虛拟空間有8頁,每頁大小為1KB,那麼虛拟位址空間就是8KB。我們假設該計算機有13條位址線,即擁有213的實體尋址能力,那麼理論上實體空間可以多達8KB。但是出于種種原因,購買記憶體的資金不夠,隻買得起6KB的記憶體,是以實體空間其實真正有效的隻是前6KB。

作業系統記憶體配置設定(隔離,分段和分頁)

那麼,當我們把程序的虛拟位址空間按頁分割,把常用的資料和代碼頁裝載到記憶體中,把不常用的代碼和資料儲存在磁盤裡,當需要用到的時候再把它從磁盤裡取出來即可。以上圖為例,我們假設有兩個程序 Process1和 Process2,它們程序中的部分虛拟頁面被映射到了實體頁面,比如 VP0、VP1和VP7映射到PP0、PP2和PP3;而有部分頁面卻在磁盤中,比如VP2和VP3位于磁盤的DP0和 DP1中;另外還有一些頁面如VP4、VP5和VP6可能尚未被用到或通路到,它們暫時處于未使用的狀态。在這裡,我們把虛拟空間的頁就叫虛拟頁(VP,Virtual Page),把實體記憶體中的頁叫做實體頁(PP,Physical Page),把磁盤中的頁叫做磁盤頁(DP,Disk Page)。圖中的線表示映射關系,我們可以看到虛拟空間的有些頁被映射到同一個實體頁,這樣就可以實作記憶體共享。

圖中Process1的 VP2和VP3不在記憶體中,但是當程序需要用到這兩個頁的時候,硬體會捕獲到這個消息,就是所謂的頁錯誤(Page Fault),然後作業系統接管程序,負責将VP2和 VP3從磁盤中讀出來并且裝入記憶體,然後将記憶體中的這兩個頁與VP2和VP3之間建立映射關系。以頁為機關來存取和交換這些資料非常友善,硬體本身就支援這種以頁為機關的操作方式。

保護也是頁映射的目的之一,簡單地說就是每個頁可以設定權限屬性,誰可以修改,誰可以通路等,而隻有作業系統有權限修改這些屬性,那麼作業系統就可以做到保護自己和保護程序

虛拟存儲的實作需要依靠硬體的支援,對于不同的CPU來說是不同的。但是幾乎所有的硬體都采用一個叫 MMU ( Memory Management Unit) 的部件來進行頁映射,如圖所示:

作業系統記憶體配置設定(隔離,分段和分頁)

在頁映射模式下,CPU發出的是Virtual Address,即我們的程式看到的是虛拟位址。經過MMU轉換以後就變成了Physical Address。一般MMU都內建在CPU内部了,不會以獨立的部件存在。

繼續閱讀