尋訪x86處理器「實模式」和「保護模式」的前世此生

    仍是把博文「CPU的內部架構和工做原理」裏的這個表搬出來,祭奠那些不該該在歲月的洪荒中被淹埋的榮耀:
html

8086的誕生,標誌着Intel 正式進入了x86時代,這是個多麼具備記念意義的日子:1978-6-8。同時,8086的誕生也是處理器內存尋址技術的第一次飛躍。 git

對於一根實際的、實實在在的、物理的、可看得見、摸得着的內存條而言,處理器把它當作8位一個字節的序列來管理和存取,每個內存字節都有一個對應的地址,咱們叫它物理地址,用地址能夠表示的長度叫作尋址空間。而CPU是如何去訪問內存單元裏的數據的方式就叫作尋址。 算法

8086CPU在內存尋址方面第一次引入了一個很是重要的概念----段。在8086以前都是4位機和8位機的天下,那是並無段的概念。當程序要訪問內存時都是要給出內存的實際物理地址,這樣在程序源代碼中就會出現不少硬編碼的物理地址。這樣的程序可想而知,難重定位,可控性弱,結構醜陋,那個年代寫這樣的程序在咱們如今看來是多麼讓人惱火的一件事兒。 數據結構

8080問世後四年也就是1978年,Intel開始設計16CPU,正常來講8086的尋址空間應該是216=64KB纔對,但Intel就恰恰不這幹,8086的目標尋址空間直指1M,也就是說8086的地址總線位寬要達到20位。如何讓16位的內部寄存器對20位的外部地址空間進行尋址,Intel的工程師們從當時的PDP-11小型機身上找到了靈感。PDP-11是美國迪吉多電腦(Digital Equipment Corp.)公司於19701980年代熱銷的16位迷你電腦,PDP-11的內存管理單元(MMU)能夠將16位地址映射到24位地址空間裏(至於人家是怎麼弄,我就真不曉得了) 架構

爲了支持分段機制,Intel8086CPU裏新增了4個寄存器,分別是代碼段CS,數據段DS,堆棧段SS和其餘ES(之後深刻介紹一下這幾個兄弟夥,這涉及到進程的在內存的運行狀況)。這樣一來,一個物理地址就由兩個部分組成,分別是「段地址」:「段內偏移量」。例如,ES=0x1000DI=0xFFFF,那麼這個數據ES:DI在內存裏的絕對物理地址就是: ui

AD(Absolute Address)=(ES)*(0x10)+(DI)=0x1FFFF 編碼

就是講段基地址左移4位而後加上段內偏移量就獲得了物理內存裏的絕對地址,通過這麼一個變換,就能夠獲得一個20位的地址,8086就能夠對20位的 1M 內存空間進行尋址了。以下:

很明顯,這種方式能夠尋址的最高地址爲0xFFFF:0xFFFF,其地址空間爲0x00000~0x10FFEF,由於8086的地址總線是20位,最大隻能訪問到1MB的物理地址空間,即物理地址空間是0x00000~0xFFFFF。當程序訪問0x100000~0x10FFEF這一段地址時,由於其邏輯上是正常的,CPU並不會認爲其訪問越界而產生異常,但這段地址確實沒有實際的物理地址與其對應,怎麼辦?此時CPU採起的策略是,對於這部分超出1M地址空間的部分,自動將其從物理0地址處開始映射。也就是說,系統計算實際物理地址時是按照對1M求模運算的方式進行的,在有些技術文獻裏你會看到這種技術被稱之爲wrap-around。仍是經過一幅圖來描述一下吧:
spa

根據前面的講解咱們能夠發現段基址有個特徵,其低4位全爲0,也就是說每一個段的起始地址必定是16的整數倍,這是分段的一個基本原則。這樣每一個段的最小長度是16字節,而最大長度只能是64KB。這裏咱們能夠計算一下,1MB的物理地址空間能劃分紅多少個段。 操作系統

若是每一個段的長度爲16字節,這樣1MB物理地址空間最多能夠劃分紅64K個段; .net

若是每一個段的長度爲64KB,那麼1MB的物理地址空間最多能劃分紅16個段。

8086這種分段基址雖然實現了尋址空間的提高,可是也帶來一些問題:

1、同一個物理地址能夠有多種表示方法。例如0x01C0:0x00000x0000:0x1C00所表示的物理地址都是0x01C00

2、地址空間缺少保護機制。對於每個由段寄存器的內容肯定的「基地址」,一個進程老是可以訪問今後開始64KB的連續地址空間,而沒法加以限制。另外一方面,能夠用來改變段寄存器內容的指令也不是什麼「特權指令」,也就是說,經過改變段寄存器的內容,一個進程能夠爲所欲爲地訪問內存中的任何一個單元,而絲絕不受限制。不能對一個進程的內存訪問加以限制,也就談不上對其餘進程以及系統自己的保護。與此相應,一個CPU若是缺少對內存訪問的限制,或者說保護,就談不上什麼內存管理,也就談不上是現代意義上的中央處理器。

總結一下:8086和後來的80186,這種只能訪問1MB地址空間的工做模式,咱們將其稱之爲「實模式」。個人理解就是「實際地址模式」,由於經過段基址和段偏移算出來的地址,通過模1MB以後得出來的地址都是實際內存的物理地址。

 

因爲8086的上述問題,1982年,Intel80286CPU裏,首次引入的地址保護的概念。也就是說80286CPU可以對內存及一些其餘外圍設備作硬件級的保護設置(實質上就是屏蔽一些地址的訪問)。自從最初的x86微處理器規格之後,它對程序開發徹底向下兼容,80286芯片被製做成啓動時繼承了之前版本芯片的特性,工做在實模式下,在這種模式下其實是關閉了新的保護功能特性,所以能使以往的軟件繼續工做在新的芯片下。後續的x86處理器都是在計算機加電啓動時都是工做在實模式下。

也就是說,在保護模式下,程序不能再隨意的訪問物理內存了,有些內存地址CPU作了明確的保護限制。

1985年80386的問世,使Intel完成了從16位到32位CPU的飛躍,這中間80286毫無疑問的就成了此次飛躍的跳板。80286的地址線已經達到24位,可尋址空間是16MB,但Intel當初設計80286時提出的目標是向下兼容,這也是Intel一向的做風,正是這種做風爲Intel後面設計80386時增添了幾根兒煩惱絲。因此,在「實模式」下,80286所表現的行爲和8086所表現的徹底同樣。

80386是32位CPU,也就是說它的ALU數據總線是32位,地址總線的位寬和CPU內部數據總線的位寬是一致的,都是32位,其尋址範圍可達4GB。若是從新設計80386的架構,其結構應該至關簡潔纔對。可是80386卻很遺憾的沒法作到這一點,做爲一個產品系列中的成員分子,80386必須繼續維持「前輩」們的那些段寄存器,必須支持實模式,同時還要支持保護模式。能夠看得出來,80386其實也不容易。

 

因此,Intel決定在80386段寄存器(CS,DS,SS,ES)的基礎上構築保護模式,而且繼續保留段寄存器爲16,同時又增添了兩個段寄存器FSGS。顯然,爲了實現保護模式,光是用段寄存器來肯定一個基地址是不夠的,至少還要有一個地址段的長度,而且還須要一些諸如訪問權限之類的其餘信息。因此,這裏須要的是一個數據結構(這個數據結構就叫作「段描述符」,之後會看到),而並不是一個單純的基地址。對此, Intel設計人員的基本思路是:

在保護模式下改變段寄存器的功能,使其從一個單純的段基址變成指向一個「段描述符」的指針。所以,當一個訪存指令發出一個內存地址時, CPU按照下面過程實現從指令中的32位邏輯地址到32位線性地址,再到物理地址的轉換:

1、首先根據指令的性質來肯定該使用哪個段寄存器,例如操做指令中的地址在代碼段CS裏,而數據指令中的地址在數據段DS裏。這一點與實地址模式相同。

2、根據段寄存器裏的內容,找到相應的「段描述符」結構。

3、而後,從「段描述符」裏獲得的纔是段基址。

4、將指令中的地址做爲偏移量,而後和段描述符結構中規定的段長度進行比較,看齊是否越界。

5、根據指令的性質和段描述符中的訪問權限來肯定當前指令操做是否越權。

6、最後纔將指令中的地址做爲偏移量,與段基址相加獲得線性地址,或者叫虛擬地址。

7、最後根據線性地址算出實際的物理地址。

因此,實模式就是80186及其以前的CPU只能尋址1MB物理地址空間,且尋到的就是實實在在的物理地址的模式,用戶程序想幹啥幹啥,沒法無天;而保護模式,就是說用戶成的程序,某些地址你是不能訪問的,或者說是有限制性的訪問,且你訪問到的地址再也不是物理地址了,而是一個虛擬的地址。這個虛擬地址要通過一系列算法處理,最終映射到實際物理地址單元裏去。

如今運行在X86CPU上的主流操做系統,如LinuxFreeBSD,Windows95之後的版本以及OS/2等都是工做在保護模式下。通常狀況下,處理器只有在上電啓動,引導階段,初始化系統時纔會進入實模式,當實模式階段的任務完成後,它就切換到了保護模式。當切換到保護模式後就很難再回到實模式了,幾乎不可能。(注意個人用詞)

    未完,待續…
相關文章
相關標籤/搜索