第九章 虛擬儲存器
9.1 物理和虛擬定址
1.實體地址
計算機系統的主存被組織成一個由M個連續的位元組大小的單元組成的陣列,每位元組都有一個唯一的實體地址PA。
根據實體地址定址的是物理定址。2.虛擬地址
虛擬儲存器被組織為一個由存放在磁碟上的N個連續的位元組大小的單元組成的陣列。
使用虛擬定址時,CPU通過生成一個虛擬地址VA來訪問主存,這個虛擬地址在被送到儲存器之前先轉換成適當的實體地址(這個過程叫做地址翻譯,相關硬體為儲存器管理單元MMU)
9.2 地址空間
1.地址空間
地址空間是一個非負整數地址的有序集合:{0,1,2,……}
2.線性地址空間
地址空間中的整數是連續的。
3.虛擬地址空間
CPU從一個有 N=2^n 個地址的地址空間中生成虛擬地址,這個地址空間成為稱為虛擬地址空間。
4.地址空間的大小
由表示最大地址所需要的位數來描述。
N=2^n:n位地址空間
主存中的每個位元組都有一個選自虛擬地址空間的虛擬地址和一個選自實體地址空間的實體地址。
9.3 虛擬儲存器作為快取的工具
虛擬儲存器——虛擬頁VP,每個虛擬頁大小為P=2^平位元組
物理儲存器——物理頁PP,也叫頁幀,大小也為P位元組。
任意時刻,虛擬頁面的集合都被分為三個不相交的子集:
- 未分配的:VM系統還沒分配/建立的頁,不佔用任何磁碟空間。
- 快取的:當前快取在物理儲存器中的已分配頁
- 未快取的:沒有快取在物理儲存器中的已分配頁
9.3.2頁表
頁表是一個資料結構,存放在物理儲存器中,將虛擬頁對映到物理頁。
1.如果設定了有效位:
地址欄位表示DRAM中相應的物理頁的起始位置,這個物理頁中快取了該虛擬頁
- 2.如果沒有設定有效位:
- (1)空地址:表示該虛擬頁未被分配
- (2)不是空地址:這個地址指向該虛擬頁在磁碟上的起始位置。
9.3.3 頁命中
9.3.4缺頁
缺頁:就是指DRAM快取不命中。
缺頁異常:會呼叫核心中的缺頁異常處理程式,選擇一個犧牲頁。
頁:虛擬儲存器的習慣說法,就是塊
交換=頁面排程:磁碟和儲存器之間傳送頁的活動
按需頁面排程:直到發生不命中時才換入頁面的策略,所有現代系統都使用這個。
9.4 虛擬儲存器作為儲存器管理的工具
9.5 虛擬儲存器作為儲存器保護的工具
9.6地址翻譯
1.當頁面命中時,CPU動作:
處理器生成虛擬地址,傳給MMU
MMU生成PTE地址,並從快取記憶體/主存請求得到他
快取記憶體/主存向MMU返回PTE
MMU構造實體地址,並把它傳給快取記憶體/主存
快取記憶體/主存返回所請求的資料給處理器。
2.處理缺頁時:
處理器生成虛擬地址,傳給MMUMMU生成PTE地址,並從快取記憶體/主存請求得到他
快取記憶體/主存向MMU返回PTE
PTE中有效位為0,觸發缺頁異常
確定犧牲頁
調入新頁面,更新PTE
返回原來的程式,再次執行導致缺頁的指令,會命中
9.6.3多級頁表
多級頁表——採用層次結構,用來壓縮頁表。
9.6.4端對端的地址翻譯
9.7案例研究
9.8 儲存器對映
9.8.2使用mmap函式的使用者級儲存器對映
1.建立新的虛擬儲存器區域
2.刪除虛擬儲存器:
9.9 動態儲存器分配
9.9.1malloc和free函式:
9.9.2分配器的要求和目標:
1.要求
處理任意請求序列
立即響應請求
只使用堆
對齊塊
不修改已分配的塊
2.目標:
最大化吞吐率(吞吐率:每個單位時間裡完成的請求數)
最大化儲存器利用率——峰值利用率最大化
9.9.3碎片
雖然有未使用的儲存器,但是不能用來滿足分配請求時,發生這種現象。
1.內部碎片
發生在一個已分配塊比有效載荷大的時候
易於量化。2.外部碎片
發生在當空閒儲存器合計起來足夠滿足一個分配請求,但是沒有一個單獨的空間塊足以處理這個請求時發生
難以量化,不可預測。
9.9.4隱式空閒連結串列
堆塊的格式:
由一個字的頭部,有效荷載,和可能的額外填充組成。
空閒塊通過頭部中的大小欄位隱含地連線著,分配器可以通過遍歷堆中所有的塊,從而間接地遍歷整個空閒塊的集合。
需要:特殊標記的結束塊。
系統對齊要求和分配器對塊格式的選擇會對分配器上的最小塊大小有強制的要求。
9.9.5放置已分配的塊——放置策略
1.首次適配
從頭開始搜尋空閒連結串列,選擇第一個合適的空閒塊2.下一次適配
從上一次搜尋的結束位置開始搜尋3.最佳適配
檢索每個空閒塊,選擇適合所需請求大小的最小空閒塊
9.9.6申請額外的堆儲存器
用到sbrk函式:
#include <unistd.h>
vid *sbrk(intptr_t incr);
成功則返回舊的brk指標,出錯為-1
9.9.7合併空閒塊
合併是針對於假碎片問題的,任何實際的分配器都必須合併相鄰的空閒塊。
- 有兩種策略:
立即合併與推遲合併
9.9.8帶邊界的合併
9.9.9實現簡單的分配器
9.9.10顯式空閒連結串列
- 1.區別
- (1)分配時間
隱式的,分配時間是塊總數的線性時間
但是顯式的,是空閒塊數量的線性時間。- (2)連結串列形式
隱式——隱式空閒連結串列
顯式——雙向連結串列,有前驅和後繼,比頭部腳部好使。 2.排序策略:
後進先出
按照地址順序維護
9.9.11分離的空閒連結串列
有兩種基本方法:
1.簡單分離儲存
每個大小類的空閒連結串列包含大小相等的塊,每個塊的大小就是這個大小類中最大元素的大小。
(1)操作
如果連結串列非空:分配其中第一塊的全部
如果連結串列為空:分配器向作業系統請求一個固定大小的額外儲存器片,將這個片分成大小相等的塊,並且連線起來成為新的空閒連結串列。
(2)優缺點
優點:時間快,開銷小
缺點:容易造成內部、外部碎片2.分離適配
每個空閒連結串列是和一個大小類相關聯的,並且被組織成某種型別的顯示或隱式連結串列,每個連結串列包含潛在的大小不同的塊,這些塊的大小是大小類的成員。
這種方法快速並且對儲存器使用很有效率。
9.10 垃圾收集
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的後繼
9.11 C程式中常見的與儲存器有關的錯誤
1.間接引用壞指標
常見錯誤:
——scanf錯誤2.讀未初始化的儲存器
常見錯誤:
——假設堆儲存器被初始化為03.允許棧緩衝區溢位
常見錯誤:
——緩衝區溢位錯誤4.假設指標和它們指向的物件是相同大小的
在遠處起作用action at distance- 5.造成錯位錯誤
- 6.引用指標,而不是它所指向的物件
- 7.誤解指標運算
- 8.引用不存在的變數
- 9.引用空堆塊中的資料
10.引起儲存器洩露
心得體會
對於最後一節C程式中常見的與儲存器有關的錯誤,看完之後就不禁回想起自己在程式設計的時候遇到的類似的問題。每次遇到這種問題的解決的方法都是去百度,然後根據百度上大家五花八門的方法一個一個試,問題解決了就解決了,沒解決便手足無措,錯誤也不知道具體是由於什麼原因而導致的。看完這一節之後,有一種原來如此的感覺,下次遇到問題就可以嘗試自己解決了。