分段機制與GDT|LDT

轉載地址:http://www.cnblogs.com/chenwb89/p/operating_system_003.html

 

 一、前言

    在《操作系統篇-淺談實模式與保護模式》中提到了兩種模式,我們說在操作系統中,其實大部分時間是待在保護模式中的。因此若想理解操作系統程序中的啓動相關的部分,必須要理解保護模式下的編程,而分段機制是保護模式編程下的基礎。而且,由於實模式與保護模式的不同,對保護模式下的分段機制更需要注意。

  二、線性地址

  在保護模式下編程,訪問內存時,需要在程序中給出段地址和偏移量,因爲分段是保護模式的基本特徵之一。傳統上,段地址和偏移地址稱爲邏輯地址,偏移地址叫做有效地址,在指令中給出有效地址的方式叫做尋址方式

  段的管理是由處理器的段部件負責進行的,段部件將段地址和偏移地址相加,得到訪問內存的地址。一般來說,段部件產生的地址就是物理地址

  在分段模型下,內存的分配是不定長的,時間長了,內存空間就會碎片化,就有可能出現一種情況:內存空間是有的,但都是小塊,無法分配給某個任務。爲了解決這個問題,在支持分頁功能後,分頁功能將物理內存空間劃分成邏輯上的頁。頁的大小是固定的,一般爲 4KB,通過使用頁,可以簡化內存管理

  如下圖所示,當頁功能開啓時,段部件產生的地址就不再是物理地址了,而是線性地址,線性地址還要經頁部件轉換後,纔是物理地址。

 

   線性地址的概念用來描述任務的地址空間。如上圖所示, 32位保護模式中,每個任務都擁有4GB 的虛擬內存空間,就像一段平直的線段,因此叫線性地址空間。相應地,由段部件產生的地址,就對應着線性地址空間上的每一個點,這就是線性地址

   三、段

  段是實現虛擬地址到線性地址轉換機制的基礎。在保護方式下,段的特徵有以下三個:段基址,段限長,段屬性。這三個特徵存儲在段描述符(segmentdescriptor)之中,用以實現從邏輯地址到線性地址的轉換。段描述符存儲在段描述符表之中,通常,我們使用段選擇符定位段描述符在這個表中的位置。每個邏輯地址由16位的段選擇符+32位的偏移量組成。

  段基址規定線性地址空間中段的開始地址。在保護模式下,段基地址長32位。因爲基地址長度與尋址地址的長度相同,所以段基地址可以是 0~4GB 範圍內的任意地址,而不像實方式下規定的邊界必須被16整除。不過,還是建議應當選取那些 16 字節對齊的地址。儘管對於 Intel 處理器來說,允許不對齊的地址,但是,對齊能夠使程序在訪問代碼和數據時的性能最大化。

  段界限規定段的大小。在保護模式下,段界限用20位表示,而且段界限可以是以字節爲單位或以4K字節爲單位。偏移量是從 0 開始遞增,段界限決定了偏移量的最大值。對於向下擴展的段,如堆棧段來說, 段界限決定了偏移量的最小值。

  段屬性我們放到下面講解描述符的時候來講。

  四、段描述符表

    和一個段有關的信息需要 8 個字節來描述,這就是段描述符( Segment Descriptor),每個段都需要一個描述符。爲了存放這些描述符,需要在內存中開闢出一段空間。在這段空間裏,所有的描述符都是挨在一起,集中存放的,這就構成一個描述符表。描述符表的長度可變,最多可以包含8K個這樣的描述符(爲什麼呢?因爲段選擇子是16位的,其中的13bit用來作index)。

  有兩種描述符表,GDT和LDT。結構如下:

   

 

   其實LDT與我們在《操作系統篇-淺談實模式與保護模式》中提到過的GDT是差不多的,區別在於(1)全局(Global)和局部(local);(2)LDT表存放在LDT類型的段之中,此時GDT必須含有LDT的段描述符;(3)LDT本身是一個段,而GDT不是。

  查找GDT在線性地址中的基地址,需要藉助GDTR;而查找LDT相應基地址,需要的是GDT中的段描述符。訪問LDT需要使用段選擇符,爲了減少訪問LDT時候的段轉換次數,LDT的段選擇符,段基址,段限長都要放在LDTR寄存器之中。

  對於操作系統來說,每個系統必須定義一個GDT,用於系統中的所有任務和程序。可選擇性定義若干個LDT。GDT本身不是一個段,而是線性地址空間的一個數據結構GDT的線性基地址和長度必須加載進GDTR之中。因爲每個描述符長度是8,所以GDT的基地址最好進行8字節對齊。

   五、段選擇符

   實模式下的 6 個段寄存器 CS、 DS、 ES、 FS、 GS 和 SS,在保護模式下叫做段選擇器。和實模式不同,保護模式的內存訪問有它自己的方式。在保護模式下,儘管訪問內存時也需要指定一個段,但傳送到段選擇器的內容不是邏輯段地址,而是段描述符在描述符表中的索引號。在保護模式下訪問一個段時,傳送到段選擇器的是段選擇符,也叫段選擇子。其結構如下圖所示:

 

   如圖所示,段選擇子由三部分組成,共16bit,第一部分是描述符的索引號,用來在描述符表中選擇一個段描述符。 TI 是描述符表指示器, TI=0 時,表示描述符在 GDT 中; TI=1 時,描述符在 LDT 中。RPL 是請求特權級,表示給出當前選擇子的那個程序的特權級別,正是該程序要求訪問這個內存段。每個程序都有特權級別。

  GDT 的線性基地址在 GDTR 中,又因爲每個描述符佔 8 字節,因此,描述符在表內的偏移地址是索引號乘以 8。當處理器在執行任何改變段選擇器的指令時(比如 pop、 mov、jmp far、 call far、 iret、 retf),就將指令中提供的索引號乘以 8 作爲偏移地址,同 GDTR 中提供的線性基地址相加,以訪問 GDT。如果沒有發現什麼問題(比如超出了 GDT 的界限),就自動將找到的描述符加載到不可見的描述符高速緩存部分。如下圖所示:

 

 加載的部分包括段的線性基地址、段界限和段的訪問屬性。此後,每當有訪問內存的指令時,就不再訪問 GDT 中的描述符,直接用當前段寄存器描述符高速緩存器提供線性基地址。

   六、段描述符

   a.描述符的結構

  首先,我們來看看描述符的結構,拿出上篇blog的圖來look一look:

  GDT的作用是用來提供段式存儲機制,這種機制是段寄存器和GDT中的描述符共同提供的。每個描述符在GDT中佔8字節,也就是 2 個雙字,或者說是 64 位。圖中,下面是低32位,上面是高32位。

   其中:

  G 位是粒度位,用於解釋段界限的含義。當 G 位是“ 0”時,段界限以字節爲單位。此時,段的擴展範圍是從 1 字節到 1 兆字節( 1B~1MB),因爲描述符中的界限值是 20 位的。相反,如果該位是“ 1”,那麼,段界限是以 4KB 爲單位的。這樣,段的擴展範圍是從 4KB到 4GB

  S 位用於指定描述符的類型( Descriptor Type)。當該位是“ 0”時,表示是一個系統段;爲“ 1”時,表示是一個代碼段或者數據段(堆棧段也是特殊的數據段)。

   DPL 表示描述符的特權級( Descriptor Privilege Level, DPL)。這兩位用於指定段的特權級。共有 4 種處理器支持的特權級別,分別是 0、 1、 2、 3,其中 0 是最高特權級別, 3 是最低特權級別。剛進入保護模式時執行的代碼具有最高特權級 0(可以看成是從處理器那裏繼承來的),這些代碼通常都是操作系統代碼,因此它的特權級別最高。每當操作系統加載一個用戶程序時,它通常都會指定一個稍低的特權級,比如 3 特權級。不同特權級別的程序是互相隔離的,其互訪是嚴格限制的,而且有些處理器指令(特權指令)只能由 0 特權級的程序來執行,爲的就是安全。這裏再次點明瞭爲何叫保護模式。

   P 是段存在位( Segment Present)。 P 位用於指示描述符所對應的段是否存在。一般來說,描述符所指示的段都位於內存中。但是,當內存空間緊張時,有可能只是建立了描述符,對應的內存空間並不存在,這時,就應當把描述符的 P 位清零,表示段並不存在。P 位是由處理器負責檢查的。每當通過描述符訪問內存中的段時,如果 P 位是“ 0”,處理器就會產生一個異常中斷。

   D/B 位是“默認的操作數大小”( Default Operation Size)或者“默認的堆棧指針大小”,又或者“上部邊界”標誌。設立該標誌位,主要是爲了能夠在 32 位處理器上兼容運行 16 位保護模式的程序。D=0 表示指令中的偏移地址或者操作數是 16 位的; D=1,指示 32 位的偏移地址或者操作數。

   舉個例子來說, 如果代碼段描述符的 D 位是 0,那麼,當處理器在這個段上執行時,將使用 16位的指令指針寄存器 IP 來取指令,否則使用 32 位的 EIP。

  對於堆棧段來說,該位被叫做“ B”位,用於在進行隱式的堆棧操作時,是使用 SP 寄存器還是ESP 寄存器。

  L 位是 64 位代碼段標誌,保留此位給 64 位處理器使用。目前,我們將此位置“ 0”即可。

  TYPE 字段共 4 位,用於指示描述符的子類型,或者說是類別。

  b.描述符類型

  對於數據段來說, 這 4 位分別是 X、 E、 W、 A 位;而對於代碼段來說,這 4 位則分別是 X、 C、 R、 A 位。如下表所示
 

 

 

   X 表示是否可以執行( eXecutable)。數據段總是不可執行的, X=0;代碼段總是可以執行的,因此, X=1。

  對於數據段來說, E 位指示段的擴展方向。 E=0 是向上擴展的,也就是向高地址方向擴展的,是普通的數據段; E=1 是向下擴展的,也就是向低地址方向擴展的,通常是堆棧段。

  W 位指示段的讀寫屬性,或者說段是否可寫, W=0 的段是不允許寫入的,否則會引發處理器異常中斷; W=1的段是可以正常寫入的。

  對於代碼段來說, C 位指示段是否爲特權級依從的( Conforming)。 C=0 表示非依從的代碼段,這樣的代碼段可以從與它特權級相同的代碼段調用,或者通過門調用; C=1 表示允許從低特權級的程序轉移到該段執行。

   R 位指示代碼段是否允許讀出。代碼段總是可以執行的,但是,爲了防止程序被破壞,它是不能寫入的。至於是否有讀出的可能,由 R 位指定。 R=0 表示不能讀出,如果企圖去讀一個 R=0 的代碼段,會引發處理器異常中斷;如果 R=1,則代碼段是可以讀出的,即可以把這個段的內容當成 ROM 一樣使用。

  也許有人會問,既然代碼段是不可讀的,那處理器怎麼從裏面取指令執行呢?事實上,這裏的R屬性並非用來限制處理器, 而是用來限制程序和指令的行爲

   數據段和代碼段的 A 位是已訪問位,用於指示它所指向的段最近是否被訪問過。在描述符創建的時候,應該清零。之後,每當該段被訪問時,處理器自動將該位置“ 1”。

   七、總結

  GDT|LDT是進入保護模式必須瞭解的基礎之一,其是保護模式中進行尋址的關鍵,也是以後理解代碼跳轉的基礎,必須要熟練。

 

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