以前從來不做筆記,好多東西學了忘,忘了學,今天開始記錄下來。
X86的分段
x86微處理器有兩種工作模式,實模式和保護模式。實模式僅僅是為了與之前產品的相容。因為linux是運行在保
護模式上,所以這裡只討論保護模式.
X86分段是將記憶體位址按段區分開來,cs,ss,ds,es,fs和gs都是段寄存器。
16位的高13位是段描述的索引號(段描述符馬上就討論),TI位指示是全域描述附表(TI=0)還是局部描述附表(TI=1),RPL是特權 0為最高3最低。
段描述符:
我們就是通過它真正去找到一段記憶體位址的。一個描述符佔8個位元組。它的結構一會兒說。
段描述符表:
你可以看成是排列在連續記憶體上的一組段描述符。分通用描述元表、局部描述符表等。
情景分析 :
假設我們現在有一段指令mov cs 8 即cs段寄存器中為
0000000000001000
索引號為1,到通用描述元表去找,特權級為0.那麼通用描述元表在哪呢。X86為保護模式增加了兩個寄存器gdtr和ldtr。這兩個寄存器分別用來存放通用描述元表和局部描述符表的基地址。如gdtr中的值為0x00020000.那麼現在根據索引為1,可以找到段描述符地址。0x00020000+8*1=0x00020008。為什麼是8,因為一個段描述就佔8個位元組。
那麼gdtr和ldtr中的值是怎麼來的呢?保護模式的運行必須要用這兩個值,所以這兩個寄存器當然不可能在保護模式下再賦值,而是在實模式下就將描述符符表製作好,然後將表基地址賦值給兩個寄存器,等到模式切換成保護模式後就可以直接用這兩個值去找到這兩個表了。
現在我們來到了段描述符了,來看看描述符的結構,段描述符的結構比較複雜,最噁心的是有好多種段描述符還多少有些區別。
Base:段首地址(之所以分在不同的地區是為了相容以前的晶片)
G: 段大小計算單位是以位元組計算還是以4096位元組倍數計算
Limit: 存放段最後一個記憶體單元的位移量 如果G=0那麼段大小為0~1MB, G=1段大小為4KB~4GB
S: 如果s=0 這是一個系統段 否則 是普通程式碼片段或資料區段
TYPE 段的類型特徵和存取許可權
DPL 描述符特權級 0~3
P: 等於0表示段當前不再主存中。Linux總是把這個值設為1,因為它從來不把整個段交換到磁碟上
D或B 這個我還沒搞懂 以後再說只要知道32位訪問為1
AVL: linux忽略這個
現在假設我們通過cs寄存器找到了在地址0x00020008處的段描述符為
這個值是我們在實模式時就設定好的,與下面的格式圖對比。
可知段基址為110000 00000010 00000000 00000000即0x30020000 ,G=0所以段長為2^20即1MB ,D=1為32地址訪問 DPL=0特權級為最高。S=1表示程式碼片段或資料區段。TYPE=1010表示程式碼片段可讀、可執行、尚未受到訪問。通過特權級比較現在找到了基地址為0x30020000的段
一個邏輯地址分為兩部分,段選擇符:位移量。段選擇符即例子中cs中存的值。通過它我們找到了一個基地址,再加上位移量就是邏輯地址對應的線性地址了。(此時我們還沒有做分頁,所以線性地址即物理地址)。至此我們通過分段實現了一個邏輯地址到線性地址的映射。
我們知道運行在作業系統上的程式翻譯成機器指令後其實就是對邏輯地址的操作。但是如果我們運行程式時,每執行一條指令都通過這樣的方式映射那效率豈不是太低了。所以x86還提供了6個附加的非編程寄存器,這些寄存器正好也是8個位元組的,當段寄存器改變時,就將通過段寄存器索引到段描述符裝入其中的一個寄存器。之後只要這個段寄存器不變,就不需要再通過選擇符去索引,直接通過這個8位元組的附加寄存器就可以找到對應的線性地址。