內存管理
包括內存管理和虛擬內存管理。
內存管理包括內存管理概念、交換與覆蓋、連續分配管理方式和非連續分配管理方式(分頁管理方式、分段管理方式、段頁式管理方式)。
虛擬內存管理包括虛擬內存概念、請求分頁管理方式、頁面置換算法、頁面分配策略、工作集和抖動。
內存管理包括內存管理概念、交換與覆蓋、連續分配管理方式和非連續分配管理方式(分頁管理方式、分段管理方式、段頁式管理方式)。
虛擬內存管理包括虛擬內存概念、請求分頁管理方式、頁面置換算法、頁面分配策略、工作集和抖動。
3.1 內存管理的概念
內存管理(Memory Management)是操作系統設計中最重要和最復雜的內容之一。雖然計算機硬件一直在飛速發展,內存容量也在不斷增長,但是仍然不可能將所有用戶進程和系統所需要的全部程序和數據放入主存中,所以操作系統必須將內存空間進行合理地划分和有效地動態分配。操作系統對內存的划分和動態分配,就是內存管理的概念。有效的內存管理在多道程序設計中非常重要,不僅方便用戶使用存儲器、提高內存利用率,還可以通過虛擬技術從邏輯上擴充存儲器。
內存管理的功能有:
- 內存空間的分配與回收:由操作系統完成主存儲器空間的分配和管理,使程序員擺脫存儲分配的麻煩,提高編程效率。
- 地址轉換:在多道程序環境下,程序中的邏輯地址與內存中的物理地址不可能一致,因此存儲管理必須提供地址變換功能,把邏輯地址轉換成相應的物理地址。
- 內存空間的擴充:利用虛擬存儲技術或自動覆蓋技術,從邏輯上擴充內存。
- 存儲保護:保證各道作業在各自的存儲空間內運行,.互不干擾。
在進行具體的內存管理之前,需要了解進程運行的基本原理和要求。
程序裝入和鏈接
創建進程首先要將程序和數據裝入內存。將用戶源程序變為可在內存中執行的程序,通常需要以下幾個步驟:- 編譯:由編譯程序將用戶源代碼編譯成若干個目標模塊。
- 鏈接:由鏈接程序將編譯后形成的一組目標模塊,以及所需庫函數鏈接在一起,形成一個完整的裝入模塊。
- 裝入:由裝入程序將裝入模塊裝入內存運行。
這三步過程如圖3-1所示。

圖3-1 對用戶程序的處理步驟
程序的鏈接有以下三種方式:
- 靜態鏈接:在程序運行之前,先將各目標模塊及它們所需的庫函數鏈接成一個完整的可執行程序,以后不再拆開。
- 裝入時動態鏈接:將用戶源程序編譯后所得到的一組目標模塊,在裝入內存時,釆用邊裝入邊鏈接的鏈接方式。
- 運行時動態鏈接:對某些目標模塊的鏈接,是在程序執行中需要該目標模塊時,才對它進行的鏈接。其優點是便於修改和更新,便於實現對目標模塊的共享。
內存的裝入模塊在裝入內存時,同樣有以下三種方式:
1) 絕對裝入。在編譯時,如果知道程序將駐留在內存的某個位置,編譯程序將產生絕對地址的目標代碼。絕對裝入程序按照裝入模塊中的地址,將程序和數據裝入內存。由於程序中的邏輯地址與實際內存地址完全相同,故不需對程序和數據的地址進行修改。
絕對裝入方式只適用於單道程序環境。另外,程序中所使用的絕對地址,可在編譯或匯編時給出,也可由程序員直接賦予。而通常情況下在程序中釆用的是符號地址,編譯或匯編時再轉換為絕對地址。
2) 可重定位裝入。在多道程序環境下,多個目標模塊的起始地址通常都是從0開始,程序中的其他地址都是相對於起始地址的,此時應釆用可重定位裝入方式。根據內存的當前情況,將裝入模塊裝入到內存的適當位置。裝入時對目標程序中指令和數據的修改過程稱為重定位,地址變換通常是在裝入時一次完成的,所以又稱為靜態重定位,如圖3-2(a) 所示。

圖3-2 重定向類型
靜態重定位的特點是在一個作業裝入內存時,必須分配其要求的全部內存空間,如果沒有足夠的內存,就不能裝入該作業。此外,作業一旦進入內存后,在整個運行期間不能在內存中移動,也不能再申請內存空間。
3) 動態運行時裝入,也稱為動態重定位,程序在內存中如果發生移動,就需要釆用動態的裝入方式。裝入程序在把裝入模塊裝入內存后,並不立即把裝入模塊中的相對地址轉換為絕對地址,而是把這種地址轉換推遲到程序真正要執行時才進行。因此,裝入內存后的所有地址均為相對地址。這種方式需要一個重定位寄存器的支持,如圖3-2(b)所示。
動態重定位的特點是可以將程序分配到不連續的存儲區中;在程序運行之前可以只裝入它的部分代碼即可投入運行,然后在程序運行期間,根據需要動態申請分配內存;便於程序段的共享,可以向用戶提供一個比存儲空間大得多的地址空間。
邏輯地址空間與物理地址空間
編譯后,每個目標模塊都是從0號單元開始編址,稱為該目標模塊的相對地址(或邏輯地址)。
當鏈接程序將各個模塊鏈接成一個完整的可執行目標程序時,鏈接程序順序依次按各個模塊的相對地址構成統一的從0號單元開始編址的邏輯地址空間。用戶程序和程序員只需知道邏輯地址,而內存管理的具體機制則是完全透明的,它們只有系統編程人員才會涉及。不同進程可以有相同的邏輯地址,因為這些相同的邏輯地址可以映射到主存的不同位置。
物理地址空間是指內存中物理單元的集合,它是地址轉換的最終地址,進程在運行時執行指令和訪問數據最后都要通過物理地址從主存中存取。當裝入程序將可執行代碼裝入內存時,必須通過地址轉換將邏輯地址轉換成物理地址,這個過程稱為地址重定位。
當CPU調度程序選擇進程執行時,派遣程序會初始化重定位寄存器和界地址寄存器。每一個邏輯地址都需要與這兩個寄存器進行核對,以保證操作系統和其他用戶程序及數據不被該進程的運行所影響。

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

圖3-3 重定位和界地址寄存器的硬件支持
3.2 內存覆蓋與內存交換
覆蓋與交換技術是在多道程序環境下用來擴充內存的兩種方法。內存覆蓋
早期的計算機系統中,主存容量很小,雖然主存中僅存放一道用戶程序,但是存儲空間放不下用戶進程的現象也經常發生,這一矛盾可以用覆蓋技術來解決。覆蓋的基本思想是:由於程序運行時並非任何時候都要訪問程序及數據的各個部分(尤其是大程序),因此可以把用戶空間分成一個固定區和若干個覆蓋區。將經常活躍的部分放在固定區,其余部分按調用關系分段。首先將那些即將要訪問的段放入覆蓋區,其他段放在外存中,在需要調用前,系統再將其調入覆蓋區,替換覆蓋區中原有的段。
覆蓋技術的特點是打破了必須將一個進程的全部信息裝入主存后才能運行的限制,但當同時運行程序的代碼量大於主存時仍不能運行。
內存交換
交換(對換)的基本思想是,把處於等待狀態(或在CPU調度原則下被剝奪運行權利)的程序從內存移到輔存,把內存空間騰出來,這一過程又叫換出;把准備好競爭CPU運行的程序從輔存移到內存,這一過程又稱為換入。中級調度就是釆用交換技術。例如,有一個CPU釆用時間片輪轉調度算法的多道程序環境。時間片到,內存管理器將剛剛執行過的進程換出,將另一進程換入到剛剛釋放的內存空間中。同時,CPU調度器可以將時間片分配給其他已在內存中的進程。每個進程用完時間片都與另一進程交換。理想情況下,內存管理器的交換過程速度足夠快,總有進程在內存中可以執行。
有關交換需要注意以下幾個問題:
- 交換需要備份存儲,通常是快速磁盤。它必須足夠大,並且提供對這些內存映像的直接訪問。
- 為了有效使用CPU,需要每個進程的執行時間比交換時間長,而影響交換時間的主要是轉移時間。轉移時間與所交換的內存空間成正比。
- 如果換出進程,必須確保該進程是完全處於空閑狀態。
- 交換空間通常作為磁盤的一整塊,且獨立於文件系統,因此使用就可能很快。
- 交換通常在有許多進程運行且內存空間吃緊時開始啟動,而系統負荷降低就暫停。
- 普通的交換使用不多,但交換策略的某些變種在許多系統中(如UNIX系統)仍發揮作用。
交換技術主要是在不同進程(或作業)之間進行,而覆蓋則用於同一個程序或進程中。由於覆蓋技術要求給出程序段之間的覆蓋結構,使得其對用戶和程序員不透明,所以對於主存無法存放用戶程序的矛盾,現代操作系統是通過虛擬內存技術來解決的,覆蓋技術則已成為歷史;而交換技術在現代操作系統中仍具有較強的生命力。
3.3 內存連續分配管理方式
連續分配方式,是指為一個用戶程序分配一個連續的內存空間。它主要包括單一連續分配、固定分區分配和動態分區分配。單一連續分配
內存在此方式下分為系統區和用戶區,系統區僅提供給操作系統使用,通常在低地址部分;用戶區是為用戶提供的、除系統區之外的內存空間。這種方式無需進行內存保護。這種方式的優點是簡單、無外部碎片,可以釆用覆蓋技術,不需要額外的技術支持。缺點是只能用於單用戶、單任務的操作系統中,有內部碎片,存儲器的利用率極低。
固定分區分配
固定分區分配是最簡單的一種多道程序存儲管理方式,它將用戶內存空間划分為若干個固定大小的區域,每個分區只裝入一道作業。當有空閑分區時,便可以再從外存的后備作業隊列中,選擇適當大小的作業裝入該分區,如此循環。
圖3-4 固定分區分配的兩種方法
固定分區分配在划分分區時,有兩種不同的方法,如圖3-4所示。
- 分區大小相等:用於利用一台計算機去控制多個相同對象的場合,缺乏靈活性。
- 分區大小不等:划分為含有多個較小的分區、適量的中等分區及少量的大分區。
為便於內存分配,通常將分區按大小排隊,並為之建立一張分區說明表,其中各表項包括每個分區的起始地址、大小及狀態(是否已分配),如圖3-5(a)所示。當有用戶程序要裝入時,便檢索該表,以找到合適的分區給予分配並將其狀態置為”已分配”;未找到合適分區則拒絕為該用戶程序分配內存。存儲空間的分配情況如圖3-5(b)所示。
這種分區方式存在兩個問題:一是程序可能太大而放不進任何一個分區中,這時用戶不得不使用覆蓋技術來使用內存空間;二是主存利用率低,當程序小於固定分區大小時,也占用了一個完整的內存分區空間,這樣分區內部有空間浪費,這種現象稱為內部碎片。
固定分區是可用於多道程序設計最簡單的存儲分配,無外部碎片,但不能實現多進程共享一個主存區,所以存儲空間利用率低。固定分區分配很少用於現在通用的操作系統中,但在某些用於控制多個相同對象的控制系統中仍發揮着一定的作用。

圖3-5 固定分區說明表和內存分配情況
動態分區分配
動態分區分配又稱為可變分區分配,是一種動態划分內存的分區方法。這種分區方法不預先將內存划分,而是在進程裝入內存時,根據進程的大小動態地建立分區,並使分區的大小正好適合進程的需要。因此系統中分區的大小和數目是可變的。
圖3-6動態分區
如圖3-6所示,系統有64MB內存空間,其中低8MB固定分配給操作系統,其余為用戶可用內存。開始時裝入前三個進程,在它們分別分配到所需空間后,內存只剩下4MB,進程4無法裝入。在某個時刻,內存中沒有一個就緒進程,CPU出現空閑,操作系統就換出進程2,換入進程4。由於進程4比進程2小,這樣在主存中就產生了一個6MB的內存塊。之后CPU又出現空閑,而主存無法容納進程2,操作系統就換出進程1,換入進程2。
動態分區在開始分配時是很好的,但是之后會導致內存中出現許多小的內存塊。隨着時間的推移,內存中會產生越來越多的碎片(圖3-6中最后的4MB和中間的6MB,且隨着進程的換入/換出,很可能會出現更多更小的內存塊),內存的利用率隨之下降。
這些小的內存塊稱為外部碎片,指在所有分區外的存儲空間會變成越來越多的碎片,這與固定分區中的內部碎片正好相對。克服外部碎片可以通過緊湊(Compaction)技術來解決,就是操作系統不時地對進程進行移動和整理。但是這需要動態重定位寄存器的支持,且相對費時。緊湊的過程實際上類似於Windows系統中的磁盤整理程序,只不過后者是對外存空間的緊湊。
在進程裝入或換入主存時,如果內存中有多個足夠大的空閑塊,操作系統必須確定分配哪個內存塊給進程使用,這就是動態分區的分配策略,考慮以下幾種算法:
在這幾種方法中,首次適應算法不僅是最簡單的,而且通常也是最好和最快的。在UNIX 系統的最初版本中,就是使用首次適應算法為進程分配內存空間,其中使用數組的數據結構 (而非鏈表)來實現。不過,首次適應算法會使得內存的低地址部分出現很多小的空閑分區,而每次分配查找時,都要經過這些分區,因此也增加了查找的開銷。
鄰近適應算法試圖解決這個問題,但實際上,它常常會導致在內存的末尾分配空間(因為在一遍掃描中,內存前面部分使用后再釋放時,不會參與分配),分裂成小碎片。它通常比首次適應算法的結果要差。
最佳適應算法雖然稱為“最佳”,但是性能通常很差,因為每次最佳的分配會留下很小的難以利用的內存塊,它會產生最多的外部碎片。
最壞適應算法與最佳適應算法相反,選擇最大的可用塊,這看起來最不容易產生碎片,但是卻把最大的連續內存划分開,會很快導致沒有可用的大的內存塊,因此性能也非常差。
Kunth和Shore分別就前三種方法對內存空間的利用情況做了模擬實驗,結果表明:
首次適應算法可能比最佳適應法效果好,而它們兩者一定比最大適應法效果好。另外注意,在算法實現時,分配操作中最佳適應法和最大適應法需要對可用塊進行排序或遍歷查找,而首次適應法和鄰近適應法只需要簡單查找;回收操作中,當回收的塊與原來的空閑塊相鄰時(有三種相鄰的情況,比較復雜),需要將這些塊合並。在算法實現時,使用數組或鏈表進行管理。除了內存的利用率,這里的算法開銷也是操作系統設計需要考慮的一個因素。
以上三種內存分區管理方法有一共同特點,即用戶進程(或作業)在主存中都是連續存放的。這里對它們進行比較和總結,見表3-1。
在進程裝入或換入主存時,如果內存中有多個足夠大的空閑塊,操作系統必須確定分配哪個內存塊給進程使用,這就是動態分區的分配策略,考慮以下幾種算法:
- 首次適應(First Fit)算法:空閑分區以地址遞增的次序鏈接。分配內存時順序查找,找到大小能滿足要求的第一個空閑分區。
- 最佳適應(Best Fit)算法:空閑分區按容量遞增形成分區鏈,找到第一個能滿足要求的空閑分區。
- 最壞適應(Worst Fit)算法:又稱最大適應(Largest Fit)算法,空閑分區以容量遞減的次序鏈接。找到第一個能滿足要求的空閑分區,也就是挑選出最大的分區。
- 鄰近適應(Next Fit)算法:又稱循環首次適應算法,由首次適應算法演變而成。不同之處是分配內存時從上次查找結束的位置開始繼續查找。
在這幾種方法中,首次適應算法不僅是最簡單的,而且通常也是最好和最快的。在UNIX 系統的最初版本中,就是使用首次適應算法為進程分配內存空間,其中使用數組的數據結構 (而非鏈表)來實現。不過,首次適應算法會使得內存的低地址部分出現很多小的空閑分區,而每次分配查找時,都要經過這些分區,因此也增加了查找的開銷。
鄰近適應算法試圖解決這個問題,但實際上,它常常會導致在內存的末尾分配空間(因為在一遍掃描中,內存前面部分使用后再釋放時,不會參與分配),分裂成小碎片。它通常比首次適應算法的結果要差。
最佳適應算法雖然稱為“最佳”,但是性能通常很差,因為每次最佳的分配會留下很小的難以利用的內存塊,它會產生最多的外部碎片。
最壞適應算法與最佳適應算法相反,選擇最大的可用塊,這看起來最不容易產生碎片,但是卻把最大的連續內存划分開,會很快導致沒有可用的大的內存塊,因此性能也非常差。
Kunth和Shore分別就前三種方法對內存空間的利用情況做了模擬實驗,結果表明:
首次適應算法可能比最佳適應法效果好,而它們兩者一定比最大適應法效果好。另外注意,在算法實現時,分配操作中最佳適應法和最大適應法需要對可用塊進行排序或遍歷查找,而首次適應法和鄰近適應法只需要簡單查找;回收操作中,當回收的塊與原來的空閑塊相鄰時(有三種相鄰的情況,比較復雜),需要將這些塊合並。在算法實現時,使用數組或鏈表進行管理。除了內存的利用率,這里的算法開銷也是操作系統設計需要考慮的一個因素。
| 作業道數 | 內部 碎片 |
外部 碎片 |
硬件支持 | 可用空 間管理 |
解決碎 片方法 |
解決空 間不足 |
提高作 業道數 |
|
|---|---|---|---|---|---|---|---|---|
| 單道連續 分配 |
1 | 有 | 無 | 界地址寄存器、越界 檢查機構 |
-- | -- | 覆蓋 | 交換 |
| 多道固定 連續分配 |
<=N (用戶空間划 為N塊) |
有 | 無 |
|
-- | -- | ||
| 多道可變連續分配 | — | 無 | 有 |
|
緊湊 |
以上三種內存分區管理方法有一共同特點,即用戶進程(或作業)在主存中都是連續存放的。這里對它們進行比較和總結,見表3-1。
3.4 內存非連續分配管理方式
非連續分配允許一個程序分散地裝入到不相鄰的內存分區中,根據分區的大小是否固定分為分頁存儲管理方式和分段存儲管理方式。分頁存儲管理方式中,又根據運行作業時是否要把作業的所有頁面都裝入內存才能運行分為基本分頁存儲管理方式和請求分頁存儲管理方式。下面介紹基本分頁存儲管理方式。
基本分頁存儲管理方式
固定分區會產生內部碎片,動態分區會產生外部碎片,這兩種技術對內存的利用率都比較低。我們希望內存的使用能盡量避免碎片的產生,這就引入了分頁的思想:把主存空間划分為大小相等且固定的塊,塊相對較小,作為主存的基本單位。每個進程也以塊為單位進行划分,進程在執行時,以塊為單位逐個申請主存中的塊空間。分頁的方法從形式上看,像分區相等的固定分區技術,分頁管理不會產生外部碎片。但它又有本質的不同點:塊的大小相對分區要小很多,而且進程也按照塊進行划分,進程運行時按塊申請主存可用空間並執行。這樣,進程只會在為最后一個不完整的塊申請一個主存塊空間時,才產生主存碎片,所以盡管會產生內部碎片,但是這種碎片相對於進程來說也是很小的,每個進程平均只產生半個塊大小的內部碎片(也稱頁內碎片)。
1) 分頁存儲的幾個基本概念
①頁面和頁面大小。進程中的塊稱為頁(Page),內存中的塊稱為頁框(Page Frame,或頁幀)。外存也以同樣的單位進行划分,直接稱為塊(Block)。進程在執行時需要申請主存空間,就是要為每個頁面分配主存中的可用頁框,這就產生了頁和頁框的一一對應。為方便地址轉換,頁面大小應是2的整數冪。同時頁面大小應該適中,如果頁面太小,會使進程的頁面數過多,這樣頁表就過長,占用大量內存,而且也會增加硬件地址轉換的開銷,降低頁面換入/換出的效率;頁面過大又會使頁內碎片增大,降低內存的利用率。所以頁面的大小應該適中,考慮到耷間效率和時間效率的權衡。
②地址結構。分頁存儲管理的邏輯地址結構如圖3-7所示。

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

圖3-8 頁表的作用
2) 基本地址變換機構
地址變換機構的任務是將邏輯地址轉換為內存中物理地址,地址變換是借助於頁表實現的。圖3-9給出了分頁存儲管理系統中的地址變換機構。
圖3-9 分頁存儲管理的地址變換機構
在系統中通常設置一個頁表寄存器(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),用來存放當前訪問的若干頁表項,以加速地址變換的過程。與此對應,主存中的頁表也常稱為慢表,配有快表的地址變換機構如圖3-10所示。

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

舉例,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大小的頁表項),理解了表3-2中的分級方式,相信對多級分頁就非常清楚了。
在圖3-12中,段號為16位,段內偏移量為16位,則一個作業最多可有2^16=65536個段,最大段長為64KB。

在頁式系統中,邏輯地址的頁號和頁內偏移量對用戶是透明的,但在段式系統中,段號和段內偏移量必須由用戶顯示提供,在髙級程序設計語言中,這個工作由編譯程序完成。

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

與分頁管理類似,分段管理的保護方法主要有兩種:一種是存取控制保護,另一種是地址越界保護。地址越界保護是利用段表寄存器中的段表長度與邏輯地址中的段號比較,若段號大於段表長度則產生越界中斷;再利用段表項中的段長和邏輯地址中的段內位移進行比較,若段內位移大於段長,也會產生越界中斷。
在段頁式系統中,作業的地址空間首先被分成若干個邏輯段,每段都有自己的段號,然后再將每一段分成若干個大小固定的頁。對內存空間的管理仍然和分頁存儲管理一樣,將其分成若干個和頁面大小相同的存儲塊,對內存的分配以存儲塊為單位,如圖3-16所示。

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

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

將頁表映射的思想進一步延伸,就可以得到二級分頁:將頁表的10頁空間也進行地址映射,建立上一級頁表,用於存儲頁表的映射關系。這里對頁表的10個頁面進行映射只需要10個頁表項,所以上一級頁表只需要1頁就足夠(可以存儲2^10=1024個頁表項)。在進程執行時,只需要將這1頁的上一級頁表調入內存即可,進程的頁表和進程本身的頁面,可以在后面的執行中再i周入內存。
如圖3-11所示,這是Intel處理器80x86系列的硬件分頁的地址轉換過程。在32位系統中,全部32位邏輯地址空間可以分為2^20(4GB/4KB)個頁面。這些頁面可以再進一步建立頂級頁表,需要2^10個頂級頁表項進行索引,這正好是一頁的大小,所以建立二級頁表即可。

圖3-11 硬件分頁地址轉換
舉例,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大小的頁表項),理解了表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兩部分組成。在圖3-12中,段號為16位,段內偏移量為16位,則一個作業最多可有2^16=65536個段,最大段長為64KB。

圖3-12 分段系統中的邏輯地址結構
在頁式系統中,邏輯地址的頁號和頁內偏移量對用戶是透明的,但在段式系統中,段號和段內偏移量必須由用戶顯示提供,在髙級程序設計語言中,這個工作由編譯程序完成。
2) 段表。
每個進程都有一張邏輯空間與內存空間映射的段表,其中每一個段表項對應進程的一個段,段表項記錄該段在內存中的起始地址和段的長度。段表的內容如圖3-13所示。
圖3-13 段表項
在配置了段表后,執行中的進程可通過查找段表,找到每個段所對應的內存區。可見,段表用於實現從邏輯段到物理內存區的映射,如圖3-14所示。

圖3-14 利用段表實現地址映射
3) 地址變換機構。
分段系統的地址變換過程如圖3-15所示。為了實現進程從邏輯地址到物理地址的變換功能,在系統中設置了段表寄存器,用於存放段表始址F和段表長度M。其從邏輯地址A到物理地址E之間的地址變換過程如下:- 從邏輯地址A中取出前幾位為段號S,后幾位為段內偏移量W。
- 比較段號S和段表長度M,若S多M,則產生越界中斷,否則繼續執行。
- 段表中段號S對應的段表項地址 = 段表起始地址F + 段號S * 段表項長度,取出該段表項的前幾位得到段長C。若段內偏移量>=C,則產生越界中斷,否則繼續執行。
- 取出段表項中該段的起始地址b,計算 E = b + W,用得到的物理地址E去訪問內存。
圖3-15 分段系統的地址變換過程
4) 段的共享與保護。
在分段系統中,段的共享是通過兩個作業的段表中相應表項指向被共享的段的同一個物理副本來實現的。當一個作業正從共享段中讀取數據時,必須防止另一個作業修改此共享段中的數據。不能修改的代碼稱為純代碼或可重入代碼(它不屬於臨界資源),這樣的代碼和不能修改的數據是可以共享的,而可修改的代碼和數據則不能共享。與分頁管理類似,分段管理的保護方法主要有兩種:一種是存取控制保護,另一種是地址越界保護。地址越界保護是利用段表寄存器中的段表長度與邏輯地址中的段號比較,若段號大於段表長度則產生越界中斷;再利用段表項中的段長和邏輯地址中的段內位移進行比較,若段內位移大於段長,也會產生越界中斷。
段頁式管理方式
頁式存儲管理能有效地提高內存利用率,而分段存儲管理能反映程序的邏輯結構並有利於段的共享。如果將這兩種存儲管理方法結合起來,就形成了段頁式存儲管理方式。在段頁式系統中,作業的地址空間首先被分成若干個邏輯段,每段都有自己的段號,然后再將每一段分成若干個大小固定的頁。對內存空間的管理仍然和分頁存儲管理一樣,將其分成若干個和頁面大小相同的存儲塊,對內存的分配以存儲塊為單位,如圖3-16所示。

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

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

圖3-18 段頁式系統的地址變換機構
