x86記憶體區段

x86架構中,記憶體區段(英語:Memory Segmentation)是在不改變16位元段選擇子時,使用單個索引暫存器(儲存了段內地址偏移值)所能夠定址的的記憶體範圍部份。也指在英特爾x86指令集體系結構下記憶體區段的實現方式。

8086開始到隨後的各款x86架構CPU,無論是真實模式還是保護模式,主記憶體定址時都使用16位元段暫存器(segment register)。段暫存器預設使用情況為:

  • 代碼段暫存器CS與暫存器IP相配合獲得當前線程代碼執行到的主記憶體位置;
  • 數據段暫存器DS與各通用暫存器配合訪問主記憶體中的數據;
  • 棧段暫存器SS與暫存器(E)SP、(E)BP配合訪問線程的呼叫棧(call stack);
  • 擴充段暫存器ES用於特定字串指令(如MOVS或CMPS)。
  • 80386引入了2個額外的段暫存器FS與GS,並無特定的硬件用途。

這些段暫存器除了有16位元的可見部分,還有不可見的隱藏部分,稱為描述符快取「descriptor cache」或隱藏暫存器「shadow register」[1]。當一個段選擇子(segment selector)裝入段暫存器的可見部分,處理器同時也把該段描述符的其它數據裝入到段暫存器的隱藏部分,這包括段開始的基地址、段長度、訪問控制資訊等。這些資訊快取到段暫存器中,避免了處理器在轉址(translate address)時花費額外的匯流排周期從段選擇子表中讀入數據。處理器指令中可以明示使用哪些段暫存器,這將替換掉預設使用的段暫存器。[2]

歷史

1978年的Intel 8086開始引入了主記憶體區段。這使得16位元CPU可以訪問超過64 KB (65,536位元組)的主記憶體,實際上8086 CPU到主記憶體的地址匯流排是20位,即可訪問220=1MB主記憶體。在16位元模式,要讓應用程式使用多個記憶體區段(為了存取比任一64K區段還要大的記憶體)是相當複雜。這個問題的根源在於沒有適當的適合做整個記憶體範圍的平面定址的地址算術指令。平面定址方式也可以乘法指令來完成,但這會導致較慢的程式執行速度。

1982年面世的80286處理器的真實模式保護模式,以及80386及其後的處理器的虛擬86模式,一個區段的大小是64 KiB(使用16位元索引暫存器)。在Intelx86真實模式下的區段架構的主記憶體空間會有所重疊,這是一種不好的設計。 80286的保護模式下,16位元的段暫存器中的13位(稱作段選擇子segment selector)是描述符表的條目(descriptor table entry)的索引;該條目包含了24位元的段開始地址以及16位元的段長度;段開始地址與段內偏移地址相加即為主記憶體實體位址。16位元段暫存器中的剩餘3位分別是全域/局部描述符表指示位、請求特權級(request privilege level)。

1985年面世的80386及其後續處理器的32位元保護模式下,一個區段長度上限是220個粒度單位,粒度可以是1位元組或4K位元組,因此區段長度上限可以是4 GB,這與索引暫存器是32位元相配合。

隨着32位元作業系統的推出,以及更舒適的32位元平面記憶體模式,到1990年末期幾乎淘汰了使用區段定址。然而,使用32位元平面記憶體模式產生的最多只能訪問4 GB地址空間的限制並沒有遠離日常的使用。區段允許作業系統對每個行程虛擬定址空間的限制,最大可利用64 GB的系統記憶體,但這種最終回歸到區段的尷尬,經常被引述為朝着64位元處理器發展的動機。

2003年問世的x86-64架構下,強制實現了平面記憶體模式,但保留了使用段暫存器FS或GS的64位元下的區段定址。

真實模式 (Real Mode)

真實模式虛擬86模式下,可訪問主記憶體固定為1 MB。對於8086處理器,可使用暫存器長度為16位元組,但是擁有20位元組的到主記憶體匯流排地址。為了使用16位元組暫存器訪問20位元組地址,整個主記憶體被劃分為多個區段。一個區段長65,536位元組即64 KB。在真實模式下,訪問主記憶體必須通過segment base address和segment offset(分別儲存在任意segment暫存器和任意通用暫存器里。) 真實訪問的實體位址可由以下公式得到:

物理地址=(segment base address*0x10) + segment offset [3]

注意同樣的實體位址可以由多個不同對的selector,offset組成。 真實模式下,任何程式都可以訪問全部主記憶體空間。沒有對主記憶體的訪問權限保護。

80286保護模式

Intel 80286處理器仍然使用16位元段暫存器與16位元的段內偏移地址,但保護模式下支援訪問224(16M)位元組的主記憶體。16位元段暫存器內不再是段地址,16位元段暫存器的高13位被稱作段選擇子(segment selector),其值是到段描述符英語segment descriptors表的索引值。段描述符中包含了24位元的段開始的基地址,20位的段長度。段開始地址與段內偏移地址相加即為主記憶體實體位址。段的長度上限為220=1M位元組。

80386保護模式

Intel 80386處理器繼續使用286的區段保護模式,但段描述符中包含了32位元的段開始的基地址。段內偏移地址也是32位元。在區段轉址與實體位址之間又增加了一層分頁(paging)轉址。區段定址是不能關閉的。分頁可以使能或關閉(enabled or disabled),如果關閉就與286保護模式一樣。如果使用分頁機制,則由段開始的基地址與段內偏移地址相加得到的是線性地址(虛地址),線性地址還需要分頁轉址才得到主記憶體實體位址。

386的段描述符中的段長度為20位,段長度的粒度可設為1位元組或212位元組。因此段長度可以為1位元組-1M位元組,或者為1×4K位元組-1M×4K位元組。段描述符的數據結構為;

  1. Byte offset inside entry.
  2. First range is the bit offset inside entry; second range is the bit offset inside byte.


386處理器增加了兩個段暫存器FS、GS,這兩個暫存器並無硬件繫結的用途。Windows作業系統在地址FS:0中儲存了當前線程資訊塊。Linux中GS指向了線程局部儲存

通過清除控制暫存器CR0中的最低位,可由386保護模式轉為真實模式。

Linux作業系統在386保護模式下把段基址設為0,段長度設為4GiB,從而模擬了平面主記憶體模型。

段暫存器名字 描述 基地址 段長度 段描述符特權級
__KERNEL_CS 內核代碼段 0 4 GiB 0
__KERNEL_DS 內核數據段 0 4 GiB 0
__USER_CS 用戶代碼段 0 4 GiB 3
__USER_DS 用戶數據段 0 4 GiB 3

x86-64的64位元模式

在x86-64體系結構64位元的long mode,段暫存器CS, SS, DS, ES強制為0。段長度強制為264。形式上還有主記憶體區段,但實際上所有主記憶體都在唯一的一個區段中。段暫存器FS、GS可以有非0值,被作業系統用於其它用途。即硬件支援如「FS:[RAX]」這樣的暫存器間接定址。

參考文獻

  1. ^ "Intel 64 and IA-32 Architectures Software Developer's Manual", Volume 3, "System Programming Guide", published in 2011, Page "Vol. 3A 3-11".
  2. ^ Intel Corporation (2004). IA-32 Intel Architecture Software Developer's Manual Volume 1: Basic Architecture頁面存檔備份,存於互聯網檔案館
  3. ^ OsDev. OsDev Segmentation. osdev.org. [2019-08-08]. (原始內容存檔於2022-05-10).