1. 程式人生 > >全域性描述符表GDT和區域性描述符表LDT

全域性描述符表GDT和區域性描述符表LDT

段描述符

       IA-32架構的處理器訪問記憶體都是採用“段基址:段內偏移地址”的形式,即使到了保護模式也不例外。其次,真實模式脆弱的安全性也是保護模式推出的重要原因。為了記憶體安全性,必須為記憶體段新增一些額外的安全屬性,如特權級、段界限、段型別等。描述記憶體段屬性的資料結構就叫段描述符。其結構定義如下所示:

       一個段描述符是8位元組大小,它描述了一個記憶體段的地址範圍和各種屬性。

       保護模式下地址匯流排寬度是32位,因此段基址需要用32位地址來表示。從上圖看出段基址欄位一共有2個部分,分別在第2~4位元組和第7位元組。這樣劃分的目的是為了相容舊式的16位CPU(段界限欄位也是如此)。

       段界限表示段邊界的擴充套件極值,即最大擴充套件到多少或最小擴充套件到多少。擴充套件方向只有上下兩種,對於資料段和程式碼段,段的擴充套件方向是向上,即從低地址向高地址擴充套件,此時的段界限用來表示段內偏移的最大值(上界);對於棧段,段的擴充套件方向是向下,即從高地址向低地址擴充套件,此時的段界限表示段內偏移的最小值(下界)。無論是向上還是向下,段界限都表示段的邊界。段界限欄位給出的只是數值,其單位(或稱粒度)則在G位中給出,G位為0則粒度為B,為1則為4KB。因此段界限邊界值的計算公式為:

                                       (段界限欄位值+1)*(粒度大小)- 1

       記憶體訪問需要用到“段基址:段內偏移地址”,段界限其實是用來限制段內偏移地址的,段內偏移地址必須位於段描述符給出的範圍之內,否則CPU會丟擲異常。任何超範圍的偏移地址都被認為是非法的,CPU會捕獲這個異常。

       屬性欄位中的type欄位用來指定段描述符的型別,而S位的數值決定了type欄位中不同位的含義。一個段描述符首先分為兩大類,要麼是系統段(S位置0),要麼是非系統段(S位置1),或稱資料段。對於CPU而言,凡是硬體執行需要用到的東西都可稱之為系統(如硬體在記憶體中的對映),凡是軟體需要用到的東西(作業系統也是軟體,對CPU而言在這一層面它與使用者程式無區別)都是資料。無視是程式碼還是資料,都是作為硬體的輸入,因此我們常說的程式碼段在段描述符中也屬於資料段(非系統段)。type欄位要和S欄位配合才能確定段描述符的確切型別,只有S欄位的值確定後type欄位的值才有意義。

段描述符的type型別
系統段 系統段型別 第3~0位 說明
3 2 1 0
未定義 0 0 0 0 保留
可用的80826 TSS 0 0 0 1 僅限286的狀態段
LDT 0 0 1 0 區域性描述符表
忙碌的80826 TSS 0 0 1 1 僅限286,type中的第1位稱為B位,若為1,則表示當前任務忙碌,由CPU將此位置1
80826呼叫門 0 1 0 0 僅限286
任務門 0 1 0 1 任務門標識(現代作業系統中很少用到)
80826中斷門 0 1 1 0 僅限286
80826陷阱門 0 1 1 1 僅限286
未定義 1 0 0 0 保留
可用的80836 TSS 1 0 0 1 386及以上的CPU的TSS
未定義 1 0 1 0 保留
忙碌的80836 TSS 1 0 1 1 386及以上的CPU的TSS
80836呼叫門 1 1 0 0 386及以上的CPU的呼叫門
未定義 1 1 0 1 保留
中斷門 1 1 1 0 386及以上的CPU的中斷門
陷阱門 1 1 1 1 386及以上的CPU的陷阱門
對於非系統段,按程式碼段和資料段劃分,這4位分別由不同的意義
非系統段 記憶體段型別

X

R C A 說明
程式碼段 1 0 0 * 只執行程式碼段
1 1 0 * 可執行、可讀程式碼段
1 0 1 * 可執行、一致性程式碼段
1 1 1 1 可執行、可讀、一致性程式碼段
資料段

X

W

R

A 說明
0 0 0 * 只讀資料段
0 1 0 * 可讀寫資料段
0 0 1 * 只讀,向下擴充套件的資料段
0 1 1 * 可讀寫,向下擴充套件的資料段

       表中的A表示Accessed,由CPU來設定,每當該段被CPU訪問過後,CPU將該段的段描述符中的A位置1。

       C表示一致性(Conforming)程式碼段,也稱為依從程式碼段。與訪問許可權有關,C為1時表示該段是一致性程式碼段,為0時則表示改段是非一致性程式碼段。

       R即Read,為1表示可讀,為0則表示不可讀。這個屬性一般用來限制指令對程式碼段的訪問,對於CPU而言,這個標誌位不起作用,也就是說即使R為0,CPU一樣可以訪問該段。

       X即Executable,用來標識該段是否可執行。

       E即Extend,用來表示段的擴充套件方向,0表示向上擴充套件(從低地址到高地址),1表示向下擴充套件(從高地址到低地址)。

       W即Writable,用來表示段是否可寫。

       DPL(Descriptor Privilege Level)欄位為描述符特權級,這是保護模式提供的安全解決方案,有4個不同等級(0~3,數值越小特權級越大)。

       AVL即Available,表示段是否可用(對CPU無效)。

       L欄位用來設定是否是64位程式碼段,L為1表示64位程式碼段,為0表示32位程式碼段。

       D/B欄位用來表示有效地址(段內偏移地址)及運算元的大小。對於程式碼段來說,此位是D位,若D為0,表示指令中的有效地址和運算元是16位,使用ip暫存器;否則為32位,使用eip暫存器。對於棧段來說,此位是B位,用來指定運算元大小,若B為0,使用的是sp暫存器,也就是棧的起始地址是16位暫存器的最大定址範圍,0xFFFF;若B為1則使用esp暫存器,即棧的起始地址是32位暫存器的最大定址範圍,0xFFFFFFFF。

全域性描述符表

       全域性描述符表(Global Descriptor Table,GDT)是保護模式所必須的資料結構,引入GDT主要是出於系統安全性、記憶體定址方式的相容等方面的考慮。

       一個段描述符只用來定義一個記憶體段,這些描述符都存放在全域性描述符表中。GDT相當於存放段描述符的陣列,而索引則是選擇子。全域性體現在多個程式都可以在GDT中定義自己的段描述符,是公用的,全域性可見。GDT在記憶體中,需要用專門的暫存器GDTR(Global Descriptor Table Register)指引CPU找到GDT。GDTR是48位的暫存器,低16位存放GDT界限,剩餘32位存放GDT起始地址。

選擇子

       在真實模式中,段暫存器存放的是段基地址,而在保護模式下段基址已經存放在段描述符中了。因此,段暫存器的作用就改為了儲存全域性描述符表的索引——選擇子。

       由於段暫存器是16位的,所以選擇子也是16位。在其低2位,即第0~1位用來儲存RPL(Requested Privilege Level),請求特權級。選擇子的第2位是TI(Table Indicator)位,用來表示選擇子是在GDT中,還是LDT中,該位為1表示在GDT中,為0表示在LDT中。選擇子的高13位,即第3~15位為描述符表的索引值。由於索引值為13位,因此最多可以索引8192個段描述符,這與GDT中最多定義8192個段描述符是吻合的。

區域性描述符表

       區域性描述符表(Local Descriptor Table,LDT)是CPU廠商為在硬體一級原生支援多工而建立的表,按照設想,一個任務對應一個LDT。但現代作業系統中很少使用LDT。

       LDT的設計廠商建議每個任務的私有記憶體段都應該放到自己的段描述符表中,即每個任務都要有自己的LDT,隨著任務的切換,也要切換到相應任務的LDT。LDT需要先在GDT中註冊一個描述符,與GDT不同的是,LDT的第0個段描述符是可用的,因為選擇子中的TI位為1才表示在LDT中索引段描述符,TI為1必然是經過顯式初始化的結果。

A20地址線

       真實模式下有地址迴繞機制,由於真實模式下的地址線是20位,最大定址空間為0x00000~0xFFFFF,超出1MB的部分在邏輯上也是正常的,CPU將超出部分自動迴繞到0地址,相當於對1MB求模。

       在8086/8088中,只有20位地址線,即A0~A19,若記憶體超過1MB,需要第21位地址線支援。對於80286後續的CPU,通過A20Gate來控制A20地址線。為了相容,後面出現的CPU即使地址線已經多出很多,但依然保留了A20Gate,需要開啟它才能訪問超過1MB的記憶體空間。