操作系統篇-分段機制與GDT|LDT


 

|| 版權聲明:本文為博主原創文章,未經博主允許不得轉載。

  一、前言

    在《操作系統篇-淺談實模式與保護模式》中提到了兩種模式,我們說在操作系統中,其實大部分時間是待在保護模式中的。因此若想理解操作系統程序中的啟動相關的部分,必須要理解保護模式下的編程,而分段機制是保護模式編程下的基礎。而且,由於實模式與保護模式的不同,對保護模式下的分段機制更需要注意。
  二、線性地址
  在保護模式下編程,訪問內存時,需要在程序中給出段地址和偏移量,因為分段是保護模式的基本特征之一。傳統上,段地址和偏移地址稱為邏輯地址,偏移地址叫做有效地址,在指令中給出有效地址的方式叫做尋址方式
  段的管理是由處理器的段部件負責進行的,段部件將段地址和偏移地址相加,得到訪問內存的地址。一般來說,段部件產生的地址就是物理地址
  在分段模型下,內存的分配是不定長的,時間長了,內存空間就會碎片化,就有可能出現一種情況:內存空間是有的,但都是小塊,無法分配給某個任務。為了解決這個問題,在支持分頁功能后,分頁功能將物理內存空間划分成邏輯上的頁。頁的大小是固定的,一般為 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是進入保護模式必須了解的基礎之一,其是保護模式中進行尋址的關鍵,也是以后理解代碼跳轉的基礎,必須要熟練。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM