標籤:
第九章 虛擬儲存空間
虛擬儲存空間是電腦系統最重要的概念之一。
虛擬儲存空間(Virtual Memory),是硬體異常,硬體地址翻譯,主存,磁碟檔案和核心軟體的完善互動,它為每個進程提供一個大的,一致的,私人地址空間。通過一個清晰的機制,虛擬儲存空間提供了三個要重的能力:
- 它將主存看成是一個儲存在磁碟上的地址空間的快取,在主存中只儲存即時區域,並根據需要在磁碟和主存之間來回傳送資料,通過這種方式,高效的使用了主存
- 它為每個進程提供了一致的地址空間,從而簡化了儲存空間管理
- 它保護了每個進程的地址空間不被其他進程破壞
9.1 物理和虛擬定址
1.電腦系統的主存被組織成一個由M個連續的位元組大小的單元組成的數組。每位元組都有一個惟一的物理地址。
2.早期的PC使用物理定址,而且諸如數位訊號處理器,嵌入式微控制器以及Cray超級電腦這樣的系統仍然繼續使用這種定址方式。而為通用電腦設計師的現代處理器使用的是虛擬定址。使用虛擬定址時,CPU通過產生一個虛擬位址來訪問主存,這個虛擬位址在被送到儲存空間之前先轉換成適當的物理地址(這個過程叫做地址翻譯,相關硬體為儲存空間嵌入式管理單元MMU)
9.2 地址空間
1.地址空間是一個非負整數地址的有序集合:{0,1,2,...},如果地址是連續的,則稱為線性地址空間。如果電腦有n位地址匯流排,在一個帶有虛擬儲存空間的系統中,CPU從一個有N = 2n個地址的地址空間中產生虛擬位址,這個地址稱為虛擬位址空間:{0,1,2,...,N-1}
2.一個地址空間的大小是由表示最大地址所需要的位元來描述的。例如:N=2^n:n位地址空間
3.一個系統還有一個物理地址空間,它與系統中實體儲存體器的M個位元組相對應:{0,1,2,...M-1}。M不要求是2的冪。
4.主存中的每個位元組都有一個選自虛擬位址空間的虛擬位址和一個選自物理地址空間的物理地址。
9.3 虛擬儲存空間作為緩衝的工具
概念上言,虛擬儲存空間被組織為一個由存放在磁碟上的N個連續的位元組大小的單元組成的數組。每位元組都有一個惟一的虛擬位址,這個惟一的虛擬位址是作為到數組的索引的。磁碟上數組的內容被緩衝到主存中。和儲存空間階層中其他緩衝一樣,磁碟(較低層)上的資料被分成塊,這些塊作為磁碟和主存(較高層)之間的傳輸單元。VM系統通過將虛擬儲存空間分割成稱為虛擬頁(virtual page,VP)的大小固定的塊,來處理這個問題。每個虛擬頁的大小為P = 2p位元組。類似地,實體儲存體器被分割成物理頁(physical page,PP),大小也為P位元組(物理頁也被稱為頁幀,page frame)。
在任意時刻,虛擬頁面的集合都被分為三個不相交的子集:
- 未分配的:VM系統還沒分配/建立的頁,不佔用任何磁碟空間。
- 緩衝的:當前緩衝在實體儲存體器中的已指派頁
- 未緩衝的:沒有緩衝在實體儲存體器中的已指派頁
1.DRAM緩衝的組織圖
- 不命中處罰很大
- 是全相聯的——任何虛擬頁都可以放在任何的物理頁中。
- 替換演算法精密
- 總是使用寫回而不是直寫。
2.頁表
存放在實體儲存體器一個資料結構,叫做頁表(page table)。頁表將虛擬頁映射到物理頁。
頁表就是一個PTE(page table entry,頁表條目)的數組。虛擬位址空間中的每個頁在頁表中的一個固定位移量處都有一個PTE。
NULL:未分配。
VP3,VP5:已指派,但是還未被緩衝。
VP1:已指派,已緩衝。
3.頁命中
- 缺頁:就是指DRAM緩衝不命中。
- 缺頁異常:會調用核心中的缺頁例外處理常式,選擇一個犧牲頁。
- 頁:虛擬儲存空間的習慣說法,就是塊
- 交換=頁面調度:磁碟和儲存空間之間傳送頁的活動
- 按需頁面調度:直到發生不命中時才換入頁面的策略,所有現代系統都使用這個。
4.缺頁
DRAM緩衝不命中稱為缺頁。
局部性原則保證了在任意時刻,程式將往往在一個較小的活動頁面集合上工作,這個集合叫做工作集/常駐集。
顛簸:工作集大小超出了實體儲存體器的大小。
9.4 虛擬儲存空間作為儲存空間管理的工具
- 作業系統為每個進程提供了一個獨立的頁表,也就是一個獨立的虛擬位址空間。
- 抖個虛擬頁面可以映射到同一個共用物理頁面上。
- 儲存空間映射:將一組連續的虛擬頁映射到任意一個檔案中的任意位置的標記法。
VM簡化了連結和載入、代碼和資料共用,以及應用程式的儲存空間分配。
9.5 虛擬儲存空間作為儲存空間保護的工具
PTE的三個許可位:
- SUP:表示進程是否必須運行在核心模式下才能訪問該頁
- READ:讀許可權
- WRITE:寫入權限
9.6 地址翻譯
1.地址翻譯
地址翻譯是一個N元素的虛擬位址空間(VAS)中的元素和一個M元素的物理地址空間(PAS)中元素之間的映射。
2.頁表基底位址暫存器
CPU中的一個控制寄存器,叫做頁表基底位址暫存器(page table base register,PTBR)指向當前頁表。N位的虛擬位址包含兩個部分:一個p位的VPO(virtual page offset,虛擬頁面位移)和一個n-p位的VPN(virtual page number)。MMU利用VPN來選擇適當的PTE。如VPN0選擇PTE0。因為物理和虛擬頁面都是p位元組的,所以PPO(physical page offset物理頁面位移)和VPO是相同的,所以,將頁表條目中PPN(physical page number,物理頁號)和虛擬位址中的VPO串聯起來,就是相應的物理地址。
3.頁面命中完全由硬體處理的,而處理缺頁要求硬體和OS核心協作完成。4.結合快取和虛擬儲存空間
大多數系統是選擇物理定址的方式來訪問快取。使用物理定址,多個進程同時在快取中有儲存塊和共用來自相同虛擬頁面的塊成為簡單的事情。而且,快取無需處理保護的問題,因為存取權限的檢查是地址翻譯過程中一部分。
5.利用TLB加速地址翻譯
在MMU中包括一個關於PTE的小的緩衝,稱為TLB。TLB是一個小的,虛擬定址的緩衝,其中每一行都儲存著一個由單個PTE組成的塊。
6.多級頁表
多級頁表——採用階層,用來壓縮頁表。
(1)以兩層頁表階層為例,好處是: 如果一級頁表中的一個PTE是空的,那麼相應的二級頁表就根本不會存在 只有一級頁表才需要總是在主存中,虛擬儲存空間系統可以在需要時建立、頁面調入或調出二級頁表,只有最經常使用的二級頁表才緩衝在主存中。(2)多級頁表的地址翻譯:
9.7 案例研究:Intel Core i7/Linux儲存空間系統
處理器包:四個核、一個大的所有核共用的L3快取和一個DDR3儲存空間控制器。
一、 Core i7地址翻譯二、Linux虛擬儲存空間系統
Linux為每個進程維持了一個單獨的虛擬位址空間。核心儲存空間包含核心中的代碼和資料結構。一部分區域對應到所有進程共用的物理頁面另一部分包含每個進程都不相同的資料。
1.Linux虛擬儲存空間地區
地區:就是已指派的虛擬儲存空間的連續片,這些頁是相關聯的。
每個存在的虛擬頁面都儲存在某個地區中。核心為系統中的每個進程維護一個單獨的任務結構task_struct:
一個具體地區的地區結構包括:
•vm_start:指向起始處•vm_end:指向結束處•vm_prot:描述這個地區包含的所有頁的讀寫許可許可權•vm_flags:是共用的還是私人的•vm_next:指向下一個地區
2.Linux缺頁異常處理
(1)虛擬位址A是否合法?
不合法,觸發段錯誤,終止進程 合法,進入下一條
(2)儲存空間訪問是否合法?即,是否有許可權?
不合法,觸發保護異常,終止程式 合法,進入下一條
(3)這時,是針對合法的虛擬位址進行合法的操作。所以:選擇一個犧牲頁面,如果被修改過就換新的並更新頁表。
9.8 儲存空間映射
Linux通過將一個虛擬儲存空間地區與一個磁碟上的對象關聯起來,以初始化這個虛擬儲存空間地區的內容的過程叫做儲存空間映射。
映射對象:
1.Unix檔案系統中的普通檔案2.匿名檔案(由核心建立,全都是二進位0)
一、共用對象和私人對象
-
共用對象
• 共用對象對於所有把它映射到自己的虛擬儲存空間進程來說都是可見的• 即使映射到多個共用地區,實體儲存體器中也只需要存放共用對象的一個拷貝。
-
私人對象
• 私人對象運用的技術:寫時拷貝• 在實體儲存體器中只儲存有私人對象的一份拷貝
fork函數就是應用了寫時拷貝技術,至於execve函數:
二、使用mmap函數的使用者級儲存空間映射1.建立新的虛擬儲存空間地區
#include <unistd.h> #include <sys/mman.h> void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); 成功返回指向映射地區的指標,若出錯則為-1
參數含義:
• start:這個地區從start開始• fd:檔案描述符• length:連續的對象片大小• offset:距檔案開始處的位移量• prot:存取權限位,具體如下:• PROT_EXEC:由可以被CPU執行的指令組成• PROT_READ:可讀• PROT_WRITE:可寫 PROT_NONE:不能被訪問• flag:由描述被映射物件類型的位組成,具體如下:•MAP_ANON:匿名對象,虛擬頁面是二進位0•MAP_PRIVATE:私人的、寫時拷貝的對象 MAP_SHARED:共用對象
2.刪除虛擬儲存空間:
include include <sys/mman.h> int munmap(void *start, size_t length); 成功返回0,失敗返回-1
從start開始刪除,由接下來length位元組組成的地區。
9.9 動態儲存裝置器分配
-
堆:動態儲存裝置器分配維護著一個進程的虛擬儲存空間地區,稱為堆。
假設它是一個請求二進位0的地區,緊接在未初始化的bss地區後開始,並向上(更高的地址)生長。有一個變數brk指向堆的頂部
-
分配器的兩種基本風格:
a.顯示分配器-malloc和free b.隱式分配器/垃圾收集器
一、malloc和free函數:
-
系統調用malloc函數,從堆中分配塊:
#include <stdlib.h>void *malloc(size_t size); 返回:成功則為指標,指向大小至少為size位元組的儲存空間塊,失敗則為NULL
-
系統調用free函數來釋放已指派的堆塊:
#include <stdlib.h>void free(void *ptr); 無傳回值
ptr參數必須指向一個從malloc、calloc或者reallov獲得的已指派塊的起始位置。
二、為什麼要使用動態儲存裝置器分配
因為經常知道程式實際運行時,它們才知道某些資料結構的大小。
三、分配器的要求和目標:
-
約束條件
• 處理任意請求序列• 立即響應請求• 只使用堆• 對齊塊• 不修改已指派的塊
-
目標:
• 最大化吞吐率(吞吐率:每個單位時間裡完成的請求數)• 最大化儲存空間利用率——峰值利用率最大化
四、片段
雖然有未使用的儲存空間,但是不能用來滿足分配請求時,發生這種現象。
-
內部片段
發生在一個已指派塊比有效載荷大的時候。
易於量化。
-
外部片段
發生在當空閑儲存空間合計起來足夠滿足一個分配請求,但是沒有一個單獨的空間塊足以處理這個請求時發生
難以量化,不可預測。
五、實現問題
一個實際的分配器要在吞吐率和利用率之間把握好平衡,需要考慮:
空閑快組織、放置、分割、合并
六、隱式空閑鏈表
-
堆塊的格式:由一個字的頭部,有效荷載,和可能的額外填充組成。
-
將堆組織成一個連續的已指派塊和空閑塊的序列:
-
空閑塊通過頭部中的大小欄位隱含地串連著,分配器可以通過遍曆堆中所有的塊,從而間接地遍曆整個空閑塊的集合。
需要:特殊標記的結束塊。
-
系統對齊要求和分配器對塊格式的選擇會對分配器上的最小塊大小有強制的要求。
七、放置已指派的塊——放置策略
-
首次適配
從頭開始搜尋空閑鏈表,選擇第一個合適的空閑塊
-
下一次適配
從上一次搜尋的結束位置開始搜尋
-
最佳適配
檢索每個空閑塊,選擇適合所需請求大小的最小空閑塊
八、分割空閑塊
九、擷取額外的堆儲存空間
用到sbrk函數:
#include
vid *sbrk(intptr_t incr);
成功則返回舊的brk指標,出錯為-1
通過將核心的brk指標增加incr來擴充和收縮堆。
十、合并空閑塊
合并是針對於假片段問題的,任何實際的分配器都必須合并相鄰的空閑塊。
有兩種策略:
• 立即合并• 延遲合并
十一、帶邊界的合并
這個合并的意思是,因為頭部的存在,所以向後合并是簡單的,但是向前合并是不方便的,所以在塊的最後加一個腳部,作為頭部的副本,就方便了合并,具體四種情況如下:
空閑塊總是需要腳部的。
十二、實現簡單的分配器
注意:
• 序言塊和結尾塊:序言塊是初始化時建立的,而且永不釋放;結尾塊是一個特殊的塊,總是以它為結束。• 有一個技巧,就是將重複使用的,操作複雜又有重複性的,這些可以定義成宏,方便使用也方便修改。• 需要注意強制類型轉換,尤其是帶指標的,非常複雜。• 因為規定了位元組對齊為雙字,就代表塊的大小是雙字的整數倍,不是的舍入到是。
十三、顯式空閑鏈表
- 區別
(1)分配時間
隱式的,分配時間是塊總數的線性時間但是顯式的,是空閑塊數量的線性時間。
(2)鏈表形式
隱式——隱式空閑鏈表顯式——雙向鏈表,有前驅和後繼,比頭部腳部好使。
-
排序策略:
• 後進先出• 按照地址順序維護
十四、分離的空閑鏈表
-
分離儲存,是一種流行的減少分配時間的方法。一般思路是將所有可能的塊大小分成一些等價類別/大小類。
-
分配器維護著一個空閑鏈表數組,每個大小類一個空閑鏈表,按照大小的升序排列。
-
有兩種基本方法:
1.簡單分離儲存
每個大小類的空閑鏈表包含大小相等的塊,每個塊的大小就是這個大小類中最大元素的大小。
(1)操作
如果鏈表非空:分配其中第一塊的全部
如果鏈表為空白:分配器向作業系統請求一個固定大小的額外儲存空間片,將這個片分成大小相等的塊,並且串連起來成為新的空閑鏈表。
(2)優缺點
優點:時間快,開銷小
缺點:容易造成內部、外部片段
2.分離適配
每個空閑鏈表是和一個大小類相關聯的,並且被組織成某種類型的顯示或隱式鏈表,每個鏈表包含潛在的大小不同的塊,這些塊的大小是大小類的成員。
這種方法快速並且對儲存空間使用很有效率。
3.夥伴系統——分離適配的特例
其中每個大小類都是2的冪
這樣,給定地址和塊的大小,很容易計算出它的夥伴的地址,也就是說:一個塊的地址和它的夥伴的地址只有一位不同。
優點:快速檢索,快速合并。
9.10 垃圾收集
垃圾收集器是一種動態儲存裝置分配器,它自動釋放程式不再需要的已指派塊,這些塊被稱為垃圾,自動回收堆儲存的過程叫做垃圾收集。
一、基本知識
垃圾收集器將儲存空間視作一張有向可達圖,只有當存在一條從任意根節點出發併到達p的有向路徑時,才說節點p是可達的,而不可達點就是垃圾。
二、Mark&Sweep垃圾收集器
-
有兩個階段:
• 標記:標記出根節點的所有可達的和已指派的後繼 • 清楚:釋放每個未被標記的已指派塊。
-
相關函數:
ptr定義為typedef void *ptr
• ptr isPtr(ptr p):如果p指向一個已指派塊中的某個字,那麼就返回一個指向這個塊的起始位置的指標b,否則返回NULL • int blockMarked(ptr b):如果已經標記了塊b,那麼就返回true • int blockAllocated(ptr b):如果塊b是已指派的,那麼久返回ture • void markBlock(ptr b):標記塊b • int length(ptr b):返回塊b的以字為單位的長度,不包括頭部 • void unmarkBlock(ptr b):將塊b的狀態由已標記的改為未標記的 • ptr nextBlock(ptr b):返回堆中塊b的後繼
三、C保守的Mark&Sweep
——平衡二叉樹
C的Mark&Sweep收集器必須是保守的,根本原因是C語言不會用類型標記來標記儲存空間位置。
9.11 C程式中常見的與儲存空間有關的錯誤
-
間接引用壞指標
常見錯誤——scanf錯誤
-
讀未初始化的儲存空間
常見錯誤——假設堆儲存空間被初始化為0
-
允許棧緩衝區溢位
常見錯誤——緩衝區溢位錯誤
-
假設指標和它們指向的對象是相同大小的
在遠處起作用action at distance
-
造成錯位錯誤
-
引用指標,而不是它所指向的對象
-
誤解指標運算
-
引用不存在的變數
-
引用空堆塊中的資料
-
引起儲存空間泄露
第9章 虛擬儲存空間 學習筆記