非連續分配允許一個程序分散地裝入到不相鄰的內存分區中,根據分區的大小是否固定分為分頁存儲管理方式和分段存儲管理方式。
基本分頁存儲管理方式
固定分區會產生內部碎片,動態分區會產生外部碎片,這兩種技術對內存的利用率都比較低。我們希望內存的使用能盡量避免碎片的產生,這就引入了分頁的思想:把主存空間划分為大小相等且固定的塊,塊相對較小,作為主存的基本單位。每個進程也以塊為單位進行划分,進程在執行時,以塊為單位逐個申請主存中的塊空間。
分頁的方法從形式上看,像分區相等的固定分區技術,分頁管理不會產生外部碎片。但它又有本質的不同點:塊的大小相對分區要小很多,而且進程也按照塊進行划分,進程運行時按塊申請主存可用空間並執行。這樣,進程只會在為最后一個不完整的塊申請一個主存塊空間時,才產生主存碎片,所以盡管會產生內部碎片,但是這種碎片相對於進程來說也是很小的,每個進程平均只產生半個塊大小的內部碎片(也稱頁內碎片)。
1) 分頁存儲的幾個基本概念
①頁面和頁面大小。進程中的塊稱為頁(Page),內存中的塊稱為頁框(Page Frame,或頁幀)。外存也以同樣的單位進行划分,直接稱為塊(Block)。進程在執行時需要申請主存空間,就是要為每個頁面分配主存中的可用頁框,這就產生了頁和頁框的一一對應。
為方便地址轉換,頁面大小應是2的整數冪。同時頁面大小應該適中,如果頁面太小,會使進程的頁面數過多,這樣頁表就過長,占用大量內存,而且也會增加硬件地址轉換的開銷,降低頁面換入/換出的效率;頁面過大又會使頁內碎片增大,降低內存的利用率。所以頁面的大小應該適中,考慮到耷間效率和時間效率的權衡。
②地址結構。分頁存儲管理的邏輯地址結構如圖1所示。

圖1 分頁存儲管理的地址結構
地址結構包含兩部分:前一部分為頁號P,后一部分為頁內偏移量W。地址長度為32 位,其中0~11位為頁內地址,即每頁大小為4KB;12~31位為頁號,地址空間最多允許有220頁。
③頁表。為了便於在內存中找到進程的每個頁面所對應的物理塊,系統為每個進程建立一張頁表,記錄頁面在內存中對應的物理塊號,頁表一般存放在內存中。
在配置了頁表后,進程執行時,通過查找該表,即可找到每頁在內存中的物理塊號。可見,頁表的作用是實現從頁號到物理塊號的地址映射,如圖2所示。

圖2 頁表的作用
2) 基本地址變換機構
地址變換機構的任務是將邏輯地址轉換為內存中物理地址,地址變換是借助於頁表實現的。圖3給出了分頁存儲管理系統中的地址變換機構。

圖3 分頁存儲管理的地址變換機構
在系統中通常設置一個頁表寄存器(PTR),存放頁表在內存的始址F和頁表長度M。進程未執行時,頁表的始址和長度存放在進程控制塊中,當進程執行時,才將頁表始址和長度存入頁表寄存器。設頁面大小為L,邏輯地址A到物理地址E的變換過程如下:
- 計算頁號P(P=A/L)和頁內偏移量W(W=A%L)。
- 比較頁號P和頁表長度M,若P >= M則產生越界中斷,否則繼續執行。
- 頁表中頁號P對應的頁表項地址 = 頁表起始地址F + 頁號P * 頁表項長度,取出該頁表項內容b,即為物理塊號。
- 計算E=b*L+W,用得到的物理地址E去訪問內存。
以上整個地址變換過程均是由硬件自動完成的。
例如,若頁面大小L為1K字節,頁號2對應的物理塊為b=8,計算邏輯地址A=2500 的物理地址E的過程如下:P=2500/1K=2,W=2500%1K=452,查找得到頁號2對應的物理塊的塊號為 8,E=8*1024+452=8644。
下面討論分頁管理方式存在的兩個主要問題:
- 每次訪存操作都需要進行邏輯地址到物理地址的轉換,地址轉換過程必須足夠快,否則訪存速度會降低;
- 每個進程引入了頁表,用於存儲映射機制,頁表不能太大,否則內存利用率會降低。
3) 具有快表的地址變換機構
由上面介紹的地址變換過程可知,若頁表全部放在內存中,則存取一個數據或一條指令至少要訪問兩次內存:一次是訪問頁表,確定所存取的數據或指令的物理地址,第二次才根據該地址存取數據或指令。顯然,這種方法比通常執行指令的速度慢了一半。
為此,在地址變換機構中增設了一個具有並行查找能力的高速緩沖存儲器——快表,又稱聯想寄存器(TLB),用來存放當前訪問的若干頁表項,以加速地址變換的過程。與此對應,主存中的頁表也常稱為慢表,配有快表的地址變換機構如圖4所示。

圖4 具有快表的地址變換機構
在具有快表的分頁機制中,地址的變換過程:
- CPU給出邏輯地址后,由硬件進行地址轉換並將頁號送入高速緩存寄存器,並將此頁號與快表中的所有頁號進行比較。
- 如果找到匹配的頁號,說明所要訪問的頁表項在快表中,則直接從中取出該頁對應的頁框號,與頁內偏移量拼接形成物理地址。這樣,存取數據僅一次訪存便可實現。
- 如果沒有找到,則需要訪問主存中的頁表,在讀出頁表項后,應同時將其存入快表,以便后面可能的再次訪問。但若快表已滿,則必須按照一定的算法對舊的頁表項進行替換。
注意:有些處理機設計為快表和慢表同時查找,如果在快表中查找成功則終止慢表的查找。
一般快表的命中率可以達到90%以上,這樣,分頁帶來的速度損失就降低到10%以下。快表的有效性是基於著名的局部性原理。
4) 兩級頁表
第二個問題:由於引入了分頁管理,進程在執行時不需要將所有頁調入內存頁框中,而只要將保存有映射關系的頁表調入內存中即可。但是我們仍然需要考慮頁表的大小。以32 位邏輯地址空間、頁面大小4KB、頁表項大小4B為例,若要實現進程對全部邏輯地址空間的映射,則每個進程需要220,約100萬個頁表項。也就是說,每個進程僅頁表這一項就需要4MB主存空間,這顯然是不切實際的。而即便不考慮對全部邏輯地址空間進行映射的情況,一個邏輯地址空間稍大的進程,其頁表大小也可能是過大的。以一個40MB的進程為例,頁表項共40KB,如果將所有頁表項內容保存在內存中,那么需要10個內存頁框來保存整個頁表。整個進程大小約為1萬個頁面,而實際執行時只需要幾十個頁面進入內存頁框就可以運行,但如果要求10個頁面大小的頁表必須全部進入內存,這相對實際執行時的幾十個進程頁面的大小來說,肯定是降低了內存利用率的;從另一方面來說,這10頁的頁表項也並不需要同時保存在內存中,因為大多數情況下,映射所需要的頁表項都在頁表的同一個頁面中。
將頁表映射的思想進一步延伸,就可以得到二級分頁:將頁表的10頁空間也進行地址映射,建立上一級頁表,用於存儲頁表的映射關系。這里對頁表的10個頁面進行映射只需要10個頁表項,所以上一級頁表只需要1頁就足夠(可以存儲210=1024個頁表項)。在進程執行時,只需要將這1頁的上一級頁表調入內存即可,進程的頁表和進程本身的頁面,可以在后面的執行中再i周入內存。
如圖5所示,這是Intel處理器80x86系列的硬件分頁的地址轉換過程。在32位系統中,全部32位邏輯地址空間可以分為220(4GB/4KB)個頁面。這些頁面可以再進一步建立頂級頁表,需要210個頂級頁表項進行索引,這正好是一頁的大小,所以建立二級頁表即可。

圖5 硬件分頁地址轉換
舉例,32位系統中進程分頁的工作過程:假定內核已經給一個正在運行的進程分配的邏輯地址空間是0x20000000到0x2003FFFF,這個空間由64個頁面組成。在進程運行時,我們不需要知道全部這些頁的頁框的物理地址,很可能其中很多頁還不在主存中。這里我們只注意在進程運行到某一頁時,硬件是如何計算得到這一頁的頁框的物理地址即可。現在進程需要讀邏輯地址0x20021406中的字節內容,這個邏輯地址按如下進行處理:
邏輯地址: 0x20021406 (0010 0000 0000 0010 0001 0100 0000 0110 B)
頂級頁表字段:0x80 (00 1000 0000 B)
二級頁表字段:0x21 (00 0010 0001B)
頁內偏移量字段:0x406 (0100 0000 0110 B)
頂級頁表字段的0x80用於選擇頂級頁表的第0x80表項,此表項指向和該進程的頁相關的二級頁表;二級頁表字段0x21用於選擇二級頁表的第0x21表項,此表項指向包含所需頁的頁框;最后的頁內偏移量字段0x406用於在目標頁框中讀取偏移量為0x406中的字節。
這是32位系統下比較實際的一個例子。看似較為復雜的例子,有助於比較深入地理解,希望讀者能自己動手計算一遍轉換過程。
建立多級頁表的目的在於建立索引,這樣不用浪費主存空間去存儲無用的頁表項,也不用盲目地順序式查找頁表項,而建立索引的要求是最高一級頁表項不超過一頁的大小。在 64位操作系統中,頁表的划分則需要重新考慮,這是很多教材和輔導書中的常見題目,但是很多都給出了錯誤的分析,需要注意。
我們假設仍然釆用4KB頁面大小。偏移量字段12位,假設頁表項大小為8B。這樣,其上一級分頁時,每個頁框只能存儲29(4KB/8B)個頁表項,而不再是210個,所以上一級頁表字段為9位。后面同理繼續分頁。64=12+9+9+9+9+9+7,所以需6級分頁才能實現索引。很多書中仍然按4B頁表項分析,雖然同樣得出6級分頁的結果,但顯然是錯誤的。這里給出兩個實際的64位操作系統的分頁級別(注意:里面沒有使用全部64位尋址,不過由於地址字節對齊的設計考慮,仍然使用8B大小的頁表項),理解了表1中的分級方式,相信對多級分頁就非常清楚了。
平台 | 頁面大小 | 尋址位數 | 分頁級數 | 具體分級 |
---|---|---|---|---|
Alpha | 8KB | 43 | 3 | 13+10+10+10 |
X86_64 | 4 KB | 48 | 4 | 12+9+9+9+9 |
基本分段存儲管理方式
分頁管理方式是從計算機的角度考慮設計的,以提高內存的利用率,提升計算機的性能, 且分頁通過硬件機制實現,對用戶完全透明;而分段管理方式的提出則是考慮了用戶和程序員,以滿足方便編程、信息保護和共享、動態增長及動態鏈接等多方面的需要。
1) 分段
段式管理方式按照用戶進程中的自然段划分邏輯空間。例如,用戶進程由主程序、兩個子程序、棧和一段數據組成,於是可以把這個用戶進程划分為5個段,每段從0開始編址,並分配一段連續的地址空間(段內要求連續,段間不要求連續,因此整個作業的地址空間是二維的)。其邏輯地址由段號S與段內偏移量W兩部分組成。
在圖6中,段號為16位,段內偏移量為16位,則一個作業最多可有216=65536個段,最大段長為64KB。

圖6 分段系統中的邏輯地址結構
在頁式系統中,邏輯地址的頁號和頁內偏移量對用戶是透明的,但在段式系統中,段號和段內偏移量必須由用戶顯示提供,在髙級程序設計語言中,這個工作由編譯程序完成。
2) 段表
每個進程都有一張邏輯空間與內存空間映射的段表,其中每一個段表項對應進程的一個段,段表項記錄該段在內存中的起始地址和段的長度。段表的內容如圖7所示。

圖7 段表項
在配置了段表后,執行中的進程可通過查找段表,找到每個段所對應的內存區。可見,段表用於實現從邏輯段到物理內存區的映射,如圖8所示。

圖8 利用段表實現地址映射
3) 地址變換機構
分段系統的地址變換過程如圖9所示。為了實現進程從邏輯地址到物理地址的變換功能,在系統中設置了段表寄存器,用於存放段表始址F和段表長度M。其從邏輯地址A到物理地址E之間的地址變換過程如下:
- 從邏輯地址A中取出前幾位為段號S,后幾位為段內偏移量W。
- 比較段號S和段表長度M,若S多M,則產生越界中斷,否則繼續執行。
- 段表中段號S對應的段表項地址 = 段表起始地址F + 段號S * 段表項長度,取出該段表項的前幾位得到段長C。若段內偏移量>=C,則產生越界中斷,否則繼續執行。
- 取出段表項中該段的起始地址b,計算 E = b + W,用得到的物理地址E去訪問內存。

圖9 分段系統的地址變換過程
4) 段的共享與保護。
在分段系統中,段的共享是通過兩個作業的段表中相應表項指向被共享的段的同一個物理副本來實現的。當一個作業正從共享段中讀取數據時,必須防止另一個作業修改此共享段中的數據。不能修改的代碼稱為純代碼或可重入代碼(它不屬於臨界資源),這樣的代碼和不能修改的數據是可以共享的,而可修改的代碼和數據則不能共享。
與分頁管理類似,分段管理的保護方法主要有兩種:一種是存取控制保護,另一種是地址越界保護。地址越界保護是利用段表寄存器中的段表長度與邏輯地址中的段號比較,若段號大於段表長度則產生越界中斷;再利用段表項中的段長和邏輯地址中的段內位移進行比較,若段內位移大於段長,也會產生越界中斷。
段頁式管理方式
頁式存儲管理能有效地提高內存利用率,而分段存儲管理能反映程序的邏輯結構並有利於段的共享。如果將這兩種存儲管理方法結合起來,就形成了段頁式存儲管理方式。
在段頁式系統中,作業的地址空間首先被分成若干個邏輯段,每段都有自己的段號,然后再將每一段分成若干個大小固定的頁。對內存空間的管理仍然和分頁存儲管理一樣,將其分成若干個和頁面大小相同的存儲塊,對內存的分配以存儲塊為單位,如圖10所示。

圖10 段頁式管理方式
在段頁式系統中,作業的邏輯地址分為三部分:段號、頁號和頁內偏移量,如圖11 所示。

圖11 段頁式系統的邏輯地址結構
為了實現地址變換,系統為每個進程建立一張段表,而每個分段有一張頁表。段表表項中至少包括段號、頁表長度和頁表起始地址,頁表表項中至少包括頁號和塊號。此外,系統中還應有一個段表寄存器,指出作業的段表起始地址和段表長度。
注意:在一個進程中,段表只有一個,而頁表可能有多個。
在進行地址變換時,首先通過段表查到頁表起始地址,然后通過頁表找到頁幀號,最后形成物理地址。如圖12所示,進行一次訪問實際需要三次訪問主存,這里同樣可以使用快表以加快查找速度,其關鍵字由段號、頁號組成,值是對應的頁幀號和保護碼。

圖12 段頁式系統的地址變換機構