保護模式
保護模式提供了實模式中所不具備的豐富多彩的內容。Pentium處理器是為保護模式而特別設計的。它內部的執行管道流水線,執行32位指令的效率優于執行16位指令。
電腦在啟動時候,Pentium被設計運行在實模式下,是為了便于電腦在啟動時候操作系統的啟動引導程序的執行。
運行在保護模式下的Intel處理器支持受保護的分段機制,同樣也支持分頁機制。這意味著地址解析會變得更加復雜。在實模式中,我們只需要在段地址上添加一個偏移地址便獲得一個直接與物理內存對應的地址值。在保護模式中,處理器要求在相應的位置加載特定的數據結構。此外段地址和偏移地址對,可能不再直接對應物理地址,好吧,讓我們繼續下面對內容...
保護模式下的分段機制
對Intel平臺上的分段機制理解的最好方法是圖片示例,來解釋它是如何實現的。一幅好的圖例,勝過長篇大論,對于這本問題,尤其如此。所以請認真仔細的看下圖1.9,并跟圖1.8做下比較。你也可以將圖1.9收藏起來,當你想看的候可以隨時看到它。
圖1.9
首先注意到保護模式中,使用了圖1.2中所有的完整Pentium寄存器。是的,我們又回到32位寄存器了。同樣,段地址寄存器不再存儲16位的段地址值。而是保存段選擇器。段選擇器是一個16位的數據結構,它包含三個字段。它的組成形式如圖1.10所示。真正重要的字段是索引(index)字段。索引字段存儲了一個描述符表的索引。索引值由0開始。
圖1.10
注釋:索引字段在段選擇器中不是地址值。它跟你在C語言中訪問數據元素時使用的索引類似。處理器取得索引值,并通過內部必要的計算將該索引值與該索引值對應的線性地址匹配。注意我這里說的線性地址(linear address),不是指物理地址。此時(保護模式下的分段),線性地址和物理地址是一致的,當啟用分頁機 制以后,它們就不一致了,記住這一點。
描述符表是一個入口數組,數組中每個入口(稱之為段描述符)描述對應內存段的相關屬性。段描述符中包含它所描述內存段的基地值。32位的偏移地址加上段描述符的基地值用以指定內存字節的內存地址。有兩種描述符表:全局描述符表(Global Descriptor Table -- GDT)和局部描述符表(Local Descriptor Table--LDT)。所有的操作系統必須具備一個GDT,但是具備一個可配置多個LDT。通常,如果一個LDT被使用,它將用于標識內段段所屬的進程。GDT的基地址保存在GDTR的系統寄存器中。同樣,LDT的基地值保存在LDTR寄存器中。當然,還有一些特定的系統指令用于裝載這些值(例如,LGDT和LLDT指令)。
注意:本書中討論的大部分操作系統都集中于使用GDT而且很少使用LDT(即使有使用LDT的地方)。
GDTR的大小為48位。GDTR有一個很明顯的特點是,它存儲兩個不同的值。第一個大小為16位的值,用于保存GDT大小,單位是字節。另一個32位的值,用于存儲GDT在物理內存中的線性基地址。如圖1.11
圖1.11
答案:處理器從段選擇器取得其指定的索引值,將該值乘以8(因為段描述符是64位的緣故,所以該值要為8字節)然后將結果加上由GTDR或者LDTR提供的基地址值。
注釋:當你在看圖1.2時,或許對另外兩個內存管理寄存器產生了疑惑,其實我并沒有忘記它們。它們對于我
們的討論話題來說并不如GDTR和LDTR這樣重要。IDTR寄存器和IR寄存器是用于管理硬件中斷和多任務的。本
書集中討論內存管理,所以不會對此類寄存器做更深入的討論。如果你對它們有興趣,我建議你去選一本我
在本章末尾提供的相關Intel的手冊資料
Bit 位 |
Type類型 |
Description 描述 |
|||
---|---|---|---|---|---|
11 |
10 |
9 |
8 |
||
0 |
0 |
0 |
0 |
data |
read-only |
0 |
0 |
0 |
1 |
data |
read-only, accessed |
0 |
0 |
1 |
0 |
data |
read-write |
0 |
0 |
1 |
1 |
data |
read-write, accessed |
0 |
1 |
0 |
0 |
data |
read-only, expand down |
0 |
1 |
0 |
1 |
data |
read-only, expand down, accessed |
0 |
1 |
1 |
0 |
data |
read-write, expand down |
0 |
1 |
1 |
1 |
data |
read-write, expand down, accessed |
1 |
0 |
0 |
0 |
code |
execute-only |
1 |
0 |
0 |
1 |
code |
execute-only, accessed |
1 |
0 |
1 |
0 |
code |
execute-read |
1 |
0 |
1 |
1 |
code |
execute-read, accessed |
1 |
1 |
0 |
0 |
code |
execute-only, conforming |
1 |
1 |
0 |
1 |
code |
execute-only, conforming, accessed |
1 |
1 |
1 |
0 |
code |
execute-read, conforming |
1 |
1 |
1 |
1 |
code |
execute-read, conforming, accessed |
圖1.13
#include<stdio.h>
void main()
{
int array[4];
int i;
for(i=0;i<100;i++)
{
array[i]=i;
printf("set array[%d]=%d\n",i);
}
return;
}
上面的代碼中有一個明目張膽的數組越界。當你運行這個程序時,程序會崩潰,Windows會顯示一個如圖1.14中所示的對話框。如果你以前還沒有見過這樣的對話框,好好看看吧。如果你在Windows上進行某種大量指針相關的程序開發,你遲早會看到它。
圖1.14
我沒有對控制寄存器說太多。跟目前這部分內容最相關的寄存器是是CR0寄存器。我們會在下一部分看到另一對寄存器。CR0寄存器的第一位(最低順序位)被稱為PE標志位(作為保護模式開啟的標志)。通過將PE標志位置1,我們將處理器切換至保護模式開啟所有我們前面討論的段保護機制。下面是一個完成這個關鍵工作的匯編代碼的片段
MOV EAX,CR0
OR AL,1
MOV CR0,EAX
另一個達到同樣目的的方法是使用特定目的的SMSW和LMSW system 指令:
SMSW AX
OR AL,1
LMSW AX
在這里,你已經反復聽到很多名詞:選擇器(selector),描述符(descriptor)等等。如果
這是你第一次聽到關于保護模式的內容。你或許還在混淆它們到底是什么。下面是一個是一個簡
的概要,有助于你記住這一些列的名詞和相互關系:
Cast Member |
Purpose |
---|---|
段選擇器 |
在描述符表中選擇一個描述符 |
段描述符 |
描述一個內存段(元數據信息) |
描述符表 |
保護一個段描述符數組 |
描述符表寄存器 |
存儲描述符表的基地址 |
如果分頁還沒有被啟用,由圖1.9中的結構所生成的最終地址同樣也是物理地址,該地址值與處理器放置到它的32位地址線的值是相同的。如果啟動了分頁,情況不會再是這樣,引導我們進入下一個部分:保護模式下的分頁機制。