分段

分段

在這裏插入圖片描述

如圖,地址空間(左)的堆和棧之間,有一塊每一使用的內部碎片,但是,如果要將整個地址空間加載到內存,內部碎片也會被加載。在比較極端的情況下,有一個很大的地址空間,而地址空間的大部分是沒有被使用的內部碎片,在加載到內存時,很可能因爲沒有足夠大的連續空間而加載失敗;即使加載成功,大量的內部碎片會造成內存極度的浪費。

在更多的情況下,地址空間的大小遠遠大於實際的物理內存,但是地址空間中使用到的空間卻不多,這樣的地址空間稱爲sparse address space(稀疏地址空間),因此,通過加載整個內存空間從而構建虛擬內存系統很不完善。

內部碎片不可避免,要解決內存浪費的問題,就是不加載內部碎片因此,不能將整個內存空間直接加載到內存中,而是需要分段

分段:泛化的基址/界限

分段,就是要將地址空間的邏輯段加載到內存,而不是將整個地址空間加載到內存。所以,內部碎片不會被加載。

在之前不分段時,要將整個地址空間加載到內存,一個CPU需要通過一個基址寄存器記錄起始的物理地址,需要一個界限寄存器記錄地址空間的長度。

一個典型的地址空間有三個邏輯段:代碼段(code)、堆段(heap)、棧段(stack)。要將這三個邏輯段裝入內存,每一個邏輯段都需要一個基址寄存器界限寄存器,對於有三個邏輯段的地址空間,MMU就需要3對基址/界限寄存器進行地址轉換。分段的基址寄存器也被叫做段寄存器

舉個例子:

在這裏插入圖片描述

當左邊的地址空間分段的加載到內存後(如右圖),段寄存器的值如下:

基址 大小(界限寄存器)
代碼 32KB 2KB
34KB 2KB
28KB
對於棧來說,它的生長方向是負增長
這裏的28KB是棧底,也就是高地址的一端
而代碼和堆是正方向增長,32KB和34B都是低地址的一端
2KB

引用哪個段

問題來了:現在給出一個虛擬地址(假設地址長度爲14bit),怎麼知道這個虛擬地址表示的內存在哪個段?

隱式方式

根據硬件來判斷:在訪問內存時,基於程序計數器,則訪問代碼段;基於棧或基址指針,則訪問棧段。

顯式方式

使用虛擬地址開始的幾位來標識不同的段。

舉例:有14位的虛擬地址。如果有三個段(代碼、堆、棧),則至少需要2位二進制標識不同的段。

因此,對虛擬地址有如下的劃分

在這裏插入圖片描述

對虛擬地址劃分的本質:將地址空間劃分

在這裏插入圖片描述

所以通過掩碼3000H,可以知道虛擬地址在哪個段。
通過掩碼0fffH,可以知道虛擬地址相對於這個段的低地址端偏移量(一定要強調開始位置,因爲對於棧來說,它的基址寄存器記錄的是高地址端)。

再次使用上面的例子:
在這裏插入圖片描述

基址 大小(界限寄存器)
代碼 32KB 2KB
34KB 2KB
28KB
對於棧來說,它的生長方向是負增長
所以棧開始的物理地址是(28KB)
而不是26KB
2KB

現在,要訪問虛擬地址爲5KB的內存單元;
5KB使用二進制表示:01 0100 0000 0000
通過01,硬件可以知道在哪個段(本例中,是在堆段),段內的偏移爲 0100 0000 0000 = 1KB

偏移量1KB<堆段限制寄存器的值(2KB),所以沒有越界
物理地址 = 基址寄存器 + 偏移量 = 34KB + 1KB = 35KB

討論一下“生長方向”

訪問虛擬地址爲15KB的內存單元。
15KB使用二進制表示:11 1100 0000 0000。
偏移量 = 1100 0000 0000 = 3KB

物理地址 = 偏移量 + 基址寄存器 = 3 + 28 = 31KB 爲什麼會出錯?

1100 0000 0000(3KB)是相對於11 0000 0000 0000(12KB)的偏移量,也就是說,是相對於“低地址端”的偏移量。
由於棧的生長方向是負的,基址寄存器記錄的是“高地址端”的棧底。

從“低地址端”到“高地址端”的塊,就是虛擬地址爲11 0000 0000 0000,大小爲4KB的一個劃分。

相對於“低地址端”的偏移量爲3KB,所以相對於“高地址端”的偏移量爲3KB-4KB=-1KB。

在這裏插入圖片描述

因爲偏移量的絕對值1KB<2KB,所以沒有越界。物理地址 = 偏移量 + 基址寄存器 = -1 + 28KB = 27KB

地址轉換總結

在計算物理地址時,一定要搞清楚是相對於哪一端(高地址或低地址),虛擬地址中的偏移量,是相對於低地址的偏移量。對於反向增長的段來說,基址寄存器記錄的是高地址端,所以虛擬地址的偏移量不能直接使用,需要轉換爲相對於高地址端的偏移;對於正向增長的段,就不用這麼麻煩。

操作系統的支持

  • 上下文切換時,保存/恢復各個段寄存器的值。
  • 管理空閒內存
  • 註冊異常處理程序

總結

分段,解決了因加載整個地址空間而引發的問題:因內部碎片太大而浪費物理內存。

分段沒有解決的問題是產生外部碎片:在物理內存的多個段之間,存在着一些沒有使用的空閒內存,它們可以被分配使用,但是,外部碎片往往空間不是很大,因此很難再被分配給其他段使用。

解決外部碎片的問題:緊湊(效率低,一般不會使用)、空閒內存管理、分頁(後面的博客討論)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章