操作系統對內存的划分和動態分配,就是內存管理的概念。有效的內存管理在多道程序設計中非常重要,不僅方便用戶使用存儲器、提高內存利用率,還可以通過虛擬技術從邏輯上擴充存儲器。內存管理的功能有:
- 內存空間的分配與回收
- 地址轉換:在多道程序環境下,程序中的邏輯地址與內存中的物理地址不可能一致,因此存儲管理必須提供地址變換功能,把邏輯地址轉換成相應的物理地址。
- 內存空間的擴充:利用虛擬存儲技術或自動覆蓋技術,從邏輯上擴充內存。
- 存儲保護:保證各道作業在各自的存儲空間內運行,互不干擾。
程序裝入和鏈接
創建進程首先要將程序和數據裝入內存。將用戶源程序變為可在內存中執行的程序,通常需要以下幾個步驟:
- 編譯:由編譯程序將用戶源代碼編譯成若干個目標模塊。
- 鏈接:由鏈接程序將編譯后形成的一組目標模塊,以及所需庫函數鏈接在一起,形成一個完整的裝入模塊。
- 裝入:由裝入程序將裝入模塊裝入內存運行。

程序的鏈接有以下三種方式:
- 靜態鏈接:在程序運行之前,先將各目標模塊及它們所需的庫函數鏈接成一個完整的可執行程序,以后不再拆開。
- 裝入時動態鏈接:將用戶源程序編譯后所得到的一組目標模塊,在裝入內存時,釆用邊裝入邊鏈接的鏈接方式。
- 運行時動態鏈接:對某些目標模塊的鏈接,是在程序執行中需要該目標模塊時,才對它進行的鏈接。其優點是便於修改和更新,便於實現對目標模塊的共享。
模塊在裝入內存時,同樣有以下三種方式:
- 絕對裝入。在編譯時,如果知道程序將駐留在內存的某個位置,編譯程序將產生絕對地址的目標代碼。絕對裝入程序按照裝入模塊中的地址,將程序和數據裝入內存。由於程序中的邏輯地址與實際內存地址完全相同,故不需對程序和數據的地址進行修改。
- 可重定位裝入。在多道程序環境下,多個目標模塊的起始地址通常都是從0開始,程序中的其他地址都是相對於起始地址的,此時應釆用可重定位裝入方式。根據內存的當前情況,將裝入模塊裝入到內存的適當位置。裝入時對目標程序中指令和數據的修改過程稱為重定位,地址變換通常是在裝入時一次完成的,所以又稱為靜態重定位。靜態重定位的特點是在一個作業裝入內存時,必須分配其要求的全部內存空間,如果沒有足夠的內存,就不能裝入該作業。此外,作業一旦進入內存后,在整個運行期間不能在內存中移動,也不能再申請內存空間。
- 動態運行時裝入,也稱為動態重定位,程序在內存中如果發生移動,就需要釆用動態的裝入方式。裝入程序在把裝入模塊裝入內存后,並不立即把裝入模塊中的相對地址轉換為絕對地址,而是把這種地址轉換推遲到程序真正要執行時才進行。因此,裝入內存后的所有地址均為相對地址,這種方式需要一個重定位寄存器的支持。動態重定位的特點是可以將程序分配到不連續的存儲區中;在程序運行之前可以只裝入它的部分代碼即可投入運行,然后在程序運行期間,根據需要動態申請分配內存;便於程序段的共享,可以向用戶提供一個比存儲空間大得多的地址空間。

邏輯地址空間與物理地址空間
編譯后,每個目標模塊都是從0號單元開始編址,稱為該目標模塊的相對地址(或邏輯地址)。當鏈接程序將各個模塊鏈接成一個完整的可執行目標程序時,鏈接程序順序依次按各個模塊的相對地址構成統一的從0號單元開始編址的邏輯地址空間。用戶程序和程序員只需知道邏輯地址,不同進程可以有相同的邏輯地址,因為這些相同的邏輯地址可以映射到主存的不同位置。
物理地址空間是指內存中物理單元的集合,它是地址轉換的最終地址,進程在運行時執行指令和訪問數據最后都要通過物理地址從主存中存取。當裝入程序將可執行代碼裝入內存時,必須通過地址轉換將邏輯地址轉換成物理地址,這個過程稱為地址重定位。
內存保護
內存分配前,需要保護操作系統不受用戶進程的影響,同時保護用戶進程不受其他用戶進程的影響。通過釆用重定位寄存器和界地址寄存器來實現這種保護。重定位寄存器含最小的物理地址值,界地址寄存器含邏輯地址值。每個邏輯地址值必須小於界地址寄存器;內存管理機構動態地將邏輯地址與界地址寄存器進行比較,如果未發生地址越界,則加上重定位寄存器的值后映射成物理地址,再送交內存單元。當CPU調度程序選擇進程執行時,派遣程序會初始化重定位寄存器和界地址寄存器。每一個邏輯地址都需要與這兩個寄存器進行核對,以保證操作系統和其他用戶程序及數據不被該進程的運行所影響。

內存覆蓋
覆蓋的基本思想是:由於程序運行時並非任何時候都要訪問程序及數據的各個部分(尤其是大程序),因此可以把用戶空間分成一個固定區和若干個覆蓋區。將經常活躍的部分放在固定區,其余部分按調用關系分段。首先將那些即將要訪問的段放入覆蓋區,其他段放在外存中,在需要調用前,系統再將其調入覆蓋區,替換覆蓋區中原有的段。內存交換
交換的基本思想是,把處於等待狀態 的程序從內存移到輔存,把內存空間騰出來,這一過程又叫換出;把准備好競爭CPU運行的程序從輔存移到內存,這一過程又稱為換入。 有關交換需要注意以下幾個問題:- 交換需要備份存儲,通常是快速磁盤。它必須足夠大,並且提供對這些內存映像的直接訪問。
- 為了有效使用CPU,需要每個進程的執行時間比交換時間長,而影響交換時間的主要是轉移時間。轉移時間與所交換的內存空間成正比。
- 如果換出進程,必須確保該進程是完全處於空閑狀態。
- 交換空間通常作為磁盤的一整塊,且獨立於文件系統,因此使用就可能很快。
- 交換通常在有許多進程運行且內存空間吃緊時開始啟動,而系統負荷降低就暫停。
內存分配
單一連續分配
內存在此方式下分為系統區和用戶區,系統區僅提供給操作系統使用,通常在低地址部分;用戶區是為用戶提供的、除系統區之外的內存空間。這種方式無需進行內存保護。這種方式的優點是簡單、無外部碎片,可以釆用覆蓋技術,不需要額外的技術支持。缺點是只能用於單用戶、單任務的操作系統中,有內部碎片,存儲器的利用率極低。固定分區分配
固定分區分配是最簡單的一種多道程序存儲管理方式,它將用戶內存空間划分為若干個固定大小的區域,每個分區只裝入一道作業。當有空閑分區時,便可以再從外存的后備作業隊列中,選擇適當大小的作業裝入該分區,如此循環。固定分區分配在划分分區時,有兩種不同的方法: 分區大小相等、分區大小不等。

動態分區分配
動態分區分配又稱為可變分區分配,是一種動態划分內存的分區方法。這種分區方法不預先將內存划分,而是在進程裝入內存時,根據進程的大小動態地建立分區,並使分區的大小正好適合進程的需要。因此系統中分區的大小和數目是可變的。
在進程裝入或換入主存時,如果內存中有多個足夠大的空閑塊,操作系統必須確定分配哪個內存塊給進程使用,這就是動態分區的分配策略,考慮以下幾種算法:
- 首次適應(First Fit)算法:空閑分區以地址遞增的次序鏈接。分配內存時順序查找,找到大小能滿足要求的第一個空閑分區。
- 最佳適應(Best Fit)算法:空閑分區按容量遞增形成分區鏈,找到第一個能滿足要求的空閑分區。
- 最壞適應(Worst Fit)算法:又稱最大適應(Largest Fit)算法,空閑分區以容量遞減的次序鏈接。找到第一個能滿足要求的空閑分區,也就是挑選出最大的分區。
- 鄰近適應(Next Fit)算法:又稱循環首次適應算法,由首次適應算法演變而成。不同之處是分配內存時從上次查找結束的位置開始繼續查找。
作業道數 | 內部 碎片 |
外部 碎片 |
硬件支持 | 可用空 間管理 |
解決碎 片方法 |
解決空 間不足 |
提高作 業道數 |
|
---|---|---|---|---|---|---|---|---|
單道連續 分配 |
1 | 有 | 無 | 界地址寄存器、越界 檢查機構 |
-- | -- | 覆蓋 | 交換 |
多道固定 連續分配 |
<=N (用戶空間划 為N塊) |
有 | 無 |
|
-- | -- | ||
多道可變連續分配 | — | 無 | 有 |
|
緊湊 |
基本分頁存儲管理方式
固定分區會產生內部碎片,動態分區會產生外部碎片,這兩種技術對內存的利用率都比較低。為了能盡量避免碎片的產生,就引入了分頁的思想: 把主存空間划分為大小相等且固定的塊,塊相對較小,作為主存的基本單位。每個進程也以塊為單位進行划分,進程在執行時,以塊為單位逐個申請主存中的塊空間。分頁的方法從形式上看,像分區相等的固定分區技術,分頁管理不會產生外部碎片。但它又有本質的不同點:塊的大小相對分區要小很多,而且進程也按照塊進行划分,進程運行時按塊申請主存可用空間並執行。這樣,進程只會在為最后一個不完整的塊申請一個主存塊空間時,才產生主存碎片,所以盡管會產生內部碎片,但是這種碎片相對於進程來說也是很小的,每個進程平均只產生半個塊大小的內部碎片(也稱頁內碎片)。1) 分頁存儲的幾個基本概念
①頁和頁大小。進程中的塊稱為頁(Page),內存中的塊稱為幀(Page Frame,或頁幀)。外存也以同樣的單位進行划分,直接稱為塊(Block)。進程在執行時需要申請主存空間,就是要為每個頁面分配主存中的可用幀,這就產生了頁和幀的一一對應。為方便地址轉換,頁大小應是2的整數冪。同時頁大小應該適中,如果面太小,會使進程的頁數過多,這樣頁表就過長,占用大量內存,而且也會增加硬件地址轉換的開銷,降低頁面換入/換出的效率;頁面過大又會使頁內碎片增大,降低內存的利用率。所以頁面的大小應該適中,考慮到耷間效率和時間效率的權衡。②地址結構。地址結構包含兩部分:前一部分為頁號P,后一部分為頁內偏移量W。地址長度為32 位,其中0~11位為頁內地址,即每頁大小為4KB;12~31位為頁號,地址空間最多允許有220頁。


2) 基本地址變換機構
地址變換的任務是將邏輯地址轉換為內存中物理地址,地址變換是借助於頁表實現的。
- 計算頁號P(P=A/L)和頁內偏移量W (W=A%L)。
- 比較頁號P和頁表長度M,若P >= M,則產生越界中斷,否則繼續執行。
- 頁表中頁號P對應的頁表項地址 = 頁表起始地址F + 頁號P * 頁表項長度,取出該頁表項內容b,即為物理塊號。
- 計算E=b*L+W,用得到的物理地址E去訪問內存。
分頁管理方式存在的兩個主要問題:
- 每次訪存操作都需要進行邏輯地址到物理地址的轉換,地址轉換過程必須足夠快,否則訪存速度會降低;
- 每個進程引入了頁表,用於存儲映射機制,頁表不能太大,否則內存利用率會降低。
3) 具有快表的地址變換機構
若頁表全部放在內存中,則存取一個數據或一條指令至少要訪問兩次內存:一次是訪問頁表,確定所存取的數據或指令的物理地址,第二次才根據該地址存取數據或指令。顯然,這種方法比通常執行指令的速度慢了一半。為此,在地址變換機構中增設了一個具有並行查找能力的高速緩沖存儲器——快表,又稱聯想寄存器(TLB),用來存放當前訪問的若干頁表項,加速地址變換的過程。與此對應,主存中的頁表也常稱為慢表。
- CPU給出邏輯地址后,由硬件進行地址轉換並將頁號送入高速緩存寄存器,並將此頁號與快表中的所有頁號進行比較。
- 如果找到匹配的頁號,說明所要訪問的頁表項在快表中,則直接從中取出該頁對應的頁框號,與頁內偏移量拼接形成物理地址。這樣,存取數據僅一次訪存便可實現。
- 如果沒有找到,則需要訪問主存中的頁表,在讀出頁表項后,應同時將其存入快表,以便后面可能的再次訪問。但若快表已滿,則必須按照一定的算法對舊的頁表項進行替換。
4) 兩級頁表
由於引入了分頁管理,進程在執行時不需要將所有頁調入內存頁框中,而只要將保存有映射關系的頁表調入內存中即可。但是仍然需要考慮頁表的大小。以32位邏輯地址空間、頁面大小4KB、頁表項大小4B為例,若要實現進程對全部邏輯地址空間的映射,則每個進程需要220, 約100萬個頁表項。也就是說,每個進程僅頁表這一項就需要4MB主存空間,這顯然是不切實際的。而即便不考慮對全部邏輯地址空間進行映射的情況,一個邏輯地址空間稍大的進程,其頁表大小也可能是過大的。以一個40MB的進程為例,頁表項共40KB,如果將所有頁表項內容保存在內存中,那么需要10個內存頁框來保存整個頁表。整個進程大小約為1萬個頁面,而實際執行時只需要幾十個頁面進入內存頁框就可以運行,但如果要求10個頁面大小的頁表必須全部進入內存,這相對實際執行時的幾十個進程頁面的大小來說,肯定是降低了內存利用率的;從另一方面來說,這10頁的頁表項也並不需要同時保存在內存中,因為大多數情況下,映射所需要的頁表項都在頁表的同一個頁面中。 將頁表映射的思想進一步延伸,就可以得到二級分頁:將頁表的10頁空間也進行地址映射,建立上一級頁表,用於存儲頁表的映射關系。這里對頁表的10個頁面進行映射只需要10個頁表項,所以上一級頁表只需要1頁就足夠(可以存儲210=1024個頁表項)。在進程執行時,只需要將這1頁的上一級頁表調入內存即可,進程的頁表和進程本身的頁面,可以在后面的執行中再調入內存。如圖3-11所示,這是Intel處理器80x86系列的硬件分頁的地址轉換過程。在32位系統中,全部32位邏輯地址空間可以分為220(4GB/4KB)個頁面。這些頁面可以再進一步建立頂級頁表,需要210個頂級頁表項進行索引,這正好是一頁的大小,所以建立二級頁表即可。

邏輯地址: 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中的字節。
建立多級頁表的目的在於建立索引,這樣不用浪費主存空間去存儲無用的頁表項,也不用盲目地順序式查找頁表項,而建立索引的要求是最高一級頁表項不超過一頁 的大小。
我們假設仍然釆用4KB頁面大小。偏移量字段12位,假設頁表項大小為8B。這樣,其上一級分頁時,每個頁框只能存儲29(4KB/8B)個頁表項,而不再是210個, 所以上一級頁表字段為9位。后面同理繼續分頁。64=12+9+9+9+9+9+7,所以需6級分頁才能實現索引。很多書中仍然按4B頁表項分析,雖然同 樣得出6級分頁的結果,但顯然是錯誤的。這里給出兩個實際的64位操作系統的分頁級別(注意:里面沒有使用全部64位尋址,不過由於地址字節對齊的設計考 慮,仍然使用8B大小的頁表項),理解了表3-2中的分級方式,相信對多級分頁就非常清楚了。
平台 | 頁面大小 | 尋址位數 | 分頁級數 | 具體分級 |
---|---|---|---|---|
Alpha | 8KB | 43 | 3 | 13+10+10+10 |
X86_64 | 4 KB | 48 | 4 | 12+9+9+9+9 |
基本分段存儲管理方式
分頁管理方式是從計算機的角度考慮設計的,以提高內存的利用率,提升計算機的性能, 且分頁通過硬件機制實現,對用戶完全透明;而分段管理方式的提出則是考慮了用戶和程序員,以滿足方便編程、信息保護和共享、動態增長及動態鏈接等多方面的需要。1) 分段
段式管理方式按照用戶進程中的自然段划分邏輯空間。例如,用戶進程由主程序、兩個子程序、棧和一段數據組成,於是可以把這個用戶進程划分為5個段,每段從0開始編址,並分配一段連續的地址空間(段內要求連續,段間不要求連續,因此整個作業的地址空間是二維的)。其邏輯地址由段號S與段內偏移量W兩部分組成。如圖段號為16位,段內偏移量為16位,則一個作業最多可有216=65536個段,最大段長為64KB。
2) 段表
每個進程都有一張邏輯空間與內存空間映射的段表,其中每一個段表項對應進程的一個段,段表項記錄該段在內存中的起始地址和段的長度。段表的內容如圖所示。

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

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

注意:在一個進程中,段表只有一個,而頁表可能有多個。
在進行地址變換時,首先通過段表查到頁表起始地址,然后通過頁表找到頁幀號,最后形成物理地址。如圖所示,進行一次訪問實際需要三次訪問主存,這里同樣可以使用快表以加快查找速度,其關鍵字由段號、頁號組成,值是對應的頁幀號和保護碼。
