本文試圖用有限的篇幅來闡述80386保護模式重要知識點。本文不是一個系統(tǒng)全面的知識介紹,您可能需要了解相關的80386匯編笨腥,微型計算機原理和C語言知識。
適合讀者:需要了解8386 CPU工作模式勇垛,適合正在學習微機原理的同學和準備閱讀Linux內核代碼的碼農脖母。想自己做一個操作系統(tǒng)的同學也可以從本文中獲得一些知識儲備。
關于80386尋址
什么是尋址
這里說的尋址(addressing)是指CPU內部如何計算物理內存地址(位置)的過程窥摄。CPU要處理的數(shù)據(jù)和代碼總是在內存中存取的(暫不討論CPU緩存)镶奉,了解一個CPU是如何定位和計算內存地址是理解CPU工作原理的基礎础淤。80386采用分段分頁來管理內存崭放,之所以沒有被設計成固定內存位置尋址(內存位置固定標記,CPU按固定地址存雀胄住)有很多原因:
為什么要分段
- 對8086來說:CPU是16位的币砂,CPU只能處理16位的地址,而其地址總線卻有20位玻侥。只有通過分段才能訪問所有地址决摧。(SS * 16 + SP最大可以表示20位地址, SS:段地址,SP:偏移地址)凑兰。之所以要設計20位地址總線掌桩,是因為8086的設計目標是支持最大1M內存。
- 分段機制不讓程序直接訪問物理內存地址姑食,可以讓程序使用的內存地址虛擬化波岛。這種機制可以讓操作系統(tǒng)更加靈活和方便管物理內存地址,增加物理內存地址利用率音半。
- 對80X86來說:保護模式中的分段分頁使得內存使用和分配更加靈活则拷。本篇中下面章節(jié)會有詳細論述。
8086尋址方式
8086中曹鸠,程序使用的地址(邏輯地址)煌茬,是由16位的段地址和16位的段內偏移組成。段地址保存在用戶程序中彻桃,程序加載時被加載在段寄存器內坛善。之后程序中指令直接使用段內偏移地址(也叫段內偏移量)即可。CPU在尋址(如何計算得到物理內存地址)時會經(jīng)過如下步驟:
- 取得該程序加載在段寄存器中的段地址;
- 取得當前指令使用的偏移地址眠屎;
- 將#1和#2中的地址使帶入公式A計算得出得到物理內存地址笙纤。
其中公式A可歸納成SS * 16 + SP。
80386尋址方式
80386與8086只是公式A 有所不同的组力。80386的邏輯地址同樣也由兩部分組成:16位段選擇子和32位段內偏移省容。尋址步驟如下:
- 取得該程序的段選擇子,同樣也是加載在段寄存器中燎字;
- 取得當前指令使用的偏移地址腥椒;
- 將#1和#2中的地址帶入公式A計算得出得到物理內存地址(如果沒有分頁)
注意:段選擇子其實同樣被加載在16位段寄存器中,只是80386中我們用了個不同的名字候衍,后面章節(jié)會解釋為什么取這個名字笼蛛。
從上面2點可以看出從8086到80386CPU尋址方式總體沒有差別。它們都是通過段寄存器和偏移地址來尋址蛉鹿,尋址方式都可表示成:
物理地址 = 段寄存器[段內偏移]
從8086到80386所不同的是滨砍,上面的步驟3中的公式A和偏移地址長度發(fā)生了變化。
8086步驟#3中的地址公式A如下:
20位的物理地址 = 段地址(16位) * 16 + 段內偏移(16位)
一般教科書都有類似這樣的表述:段地址左移四位+偏移地址形成20位物理地址妖异。左移4位相當于乘以16(2的4次方)惋戏,這里16也經(jīng)常被寫成16進制形式10h。
而80386步驟#3中的地址公式A是這樣的:
- 根據(jù)段選擇子內容找到段基地址(32位)
- 用下面公式計算得到32位物理地址:
32位物理地址 = 段基地址(32位) + 段內偏移(32位)
之所以在80386中他膳,段地址叫選擇子是因為段基地址是由段寄存器中的內容選擇指定的响逢。
需要指出的是80386還多了一個分頁的功能,這個功能是可選的棕孙。如果啟用分頁功能(本文最后會對分頁詳細描述)舔亭,通過上面步驟計算得到地址叫做線性地址,CPU要進一步處理才能得到最終的物理地址蟀俊。
重要寄存器和數(shù)據(jù)結構
本節(jié)內容需要花一定的時間去記憶和理解钦铺,難點在于只有同時理解并記住這些概念才能融匯貫通。這只有反復閱讀和思考才能做到肢预。建議閱讀中遇到難以理解的問題可以先了解個大概后跳過矛洞,等到需要理解具體相關內容時,再返回來仔細閱讀和理解误甚。
描述符(Descriptor)
具有固定長度的結構體(struct)缚甩,共8個字節(jié)。這個結構體保存著一個段基地址的所有信息窑邦,包含:
- 段基地址
- 段長度
- 段屬性
描述符有兩種擅威,這兩種結構體包含的內容幾乎都一樣,只有一些細小的差別冈钦,這兩種描述符分別是:
- 非系統(tǒng)描述符:用來描述數(shù)據(jù)段郊丛,代碼段和堆棧段的結構;
- 系統(tǒng)段描述符:用來描述LDT和TSS的,(LDT和TSS在后面有說明)厉熟。
全局描述符表(GDT: Global Descripter Table)
顧名思義导盅,它是一個表結構。這個表存儲在內存中揍瑟,相當于C語言中的一個結構體數(shù)組白翻。數(shù)組的每一項就是上面所說的描述符。GDT在一個多任務系統(tǒng)中一般只設置一個绢片,其基址由一個GDTR確定(GDT靠GDTR定位)滤馍,GDT地址在設置好之后幾乎不會被改變。這個表中可以包含如下四種信息的描述符:
- 全局數(shù)據(jù)段底循,代碼段和堆棧段信息巢株。這些段一般由操作系統(tǒng)內核使用。
- 對LDT的描述熙涤,這個描述符的基址就是是LDT所在內存中的起始地址
- 對TSS的描述阁苞,這個描述符的基址是TSS所在內存中的起始地址
- 一些門描述符(調用門,中斷門等…)祠挫。
其中#1屬于非系統(tǒng)段描述符那槽,#2 #3 #4屬于系統(tǒng)段描述符。描述符各自的屬性值決定了它們具體是哪類描述符茸歧。其中倦炒,LDT显沈,TSS屬于具體每個任務软瞎,一般成對出現(xiàn)在GDT中。
全局描述符表寄存器(GDTR:Global Descripter Table Register)
GDTR是一個CPU寄存器拉讯,和AX BX.. CS DS...一個概念涤浇。GDTR共48位,包含兩部分內容:
- 開頭32位用來保存一個內存地址魔慷,指出GDT在內存中的位置(如果沒有開啟分頁只锭,它就是一個32位的物理地址);
- 隨后16位為GDT的長度信息院尔,即GDT共有有多少項蜻展。
局部描述符表(LDT:Local Descripter Table)
與GDT的結構類似,所不同的是邀摆,LDT用來描述每個具體用戶任務代碼段纵顾,堆棧段和數(shù)據(jù)段信息。LTD是針對每個用戶任務的栋盹,類似TSS這樣的全局信息相關的描述項自然只存在GDT中施逾,LDT中不會有。LDT描述項一般和正在運行的用戶任務數(shù)相等。每個用戶任務都可能有自己的LDT汉额,保存著本任務相關信息曹仗。LDT的基址作為一條記錄保存在GDT中(參見上面GDT#2)。
局部描述符表寄存器(LDTR:Global Descripter Table Register)
LDTR也是一個寄存器蠕搜,和GDTR類似怎茫,不同的是它只有16位。LDTR中存放的是一個16位選擇子妓灌,尋址時用選擇子內容去GDT中定位尋找LDT的基址遭居。LDTR當作為選擇子,任務切換時只要改變其中存放的選擇子內容就能實現(xiàn)LDT的切換旬渠。
任務寄存器(TR:Task Register)
也是一個16為的選擇子俱萍,作用和LDTR類似,都是用來索引全局描述符表(GDT)中的一項告丢。所不同的是TR選擇和指向的是一個任務狀態(tài)段地址(TSS:Task Status Segment)枪蘑。
任務狀態(tài)段(TSS:Task State Segment)
正如前文所說,任務狀態(tài)段(TSS)信息是在GDT中描述的岖免。任務狀態(tài)段也是是內存中的一個數(shù)據(jù)結構岳颇。這個結構中保存著和任務相關的信息。當運行著的任務準備切換時颅湘,CPU會把當前任務用到的寄存器內容(CS EIP DS SS...)包括LDT的選擇子等信息保存在TSS中以便任務切換回來時候繼續(xù)使用话侧。
選擇子(Selector)
前文相關內容已經(jīng)多次提及選擇子,選擇子按照用途共有三種闯参,其格式完全一樣瞻鹏,僅僅用途不同。
- TR中的選擇子鹿寨,指向GDT中一個TSS的描述項新博。
- LDTR中的選擇子,指向GDT中一個LDT的描述項脚草。
- 用戶程序中的邏輯地址組成部分(這個地址即虛擬地址48位=16位選擇子+32位偏移地址)赫悄。它用來選擇程序用到的數(shù)據(jù)段,代碼段等在LDT中的描述項馏慨。此處的選擇子由編譯埂淮,連接或者操作系統(tǒng)決定的。
80386中除了上述寄存器以外写隶,還有一些用戶程序不可訪問的高速緩沖寄存器寄存器倔撞,為的是提高CPU計算性能。為了簡化問題我們暫時可以忽略它的存在樟澜。
分頁機制
控制寄存器(Control Registor)CR3中保存著一個頁基址A误窖,如果分頁被啟用叮盘,線性地址經(jīng)需要經(jīng)過兩級頁變換成最終的物理地址:具體過程見下圖.
參見上圖,線性地址從高到底被分成三個部分高10位B霹俺,中間10位C柔吼,末12位D。變換過程不再描述丙唧,語言描述可能看上去復雜愈魏,圖表相對清晰,這里結合上圖給出一個變換過程公式想际,結合上圖應該可以比較清楚的看出分頁機制培漏。
頁基址 = A;
頁基址 = 頁基址 + B * 4; /* 查一級目錄頁表,在(頁基址+B*4)處取得二級頁表的的基址,這里等號代表查表 */
頁基址 = 頁基址 + C * 4; /* 查二級目錄頁表,在(頁基址+B*4)處取得物理基址,這里等號代表查表 */
物理地址 = 物理基址 + D;
上述公式中B和C為什么要乘以4?因為頁表按4位對齊的胡本。頁目錄項后四位不用牌柄。
類似的,GDT/LDT中一項長度為8字節(jié)侧甫,選擇子去GDT/LDT中索引如下:
描述符地址 = Base + Selector * 8.
80386內存管理和任務切換過程
一個80386操作系系統(tǒng)中運行兩個用戶任務A和B珊佣,如圖:
任務A運行時CPU和內存狀態(tài)如下
切換到任務B運行時,CPU和內存狀態(tài)如下
為了簡化問題披粟,上圖沒有畫出隱藏寄存器的使用情況咒锻。實際上,TR守屉,LDTR之后都有一個64位(內容和描述符一樣)程序不可訪問的高速緩存寄存器惑艇。內容為當前選擇子對應的描述符。此后到訪問拇泛,CPU只要直接讀取高速緩存寄存器中保存的某個和TR或者LDTR相對應的描述符即可滨巴,而不用再去內存中去尋找。這樣可以加快用戶任務執(zhí)行代碼和數(shù)據(jù)尋址的速度碰镜。
結束語
CPU工作原理是一個涉及很多計算機基礎知識的內容兢卵,只有反復思考總結才能理解和融合貫通。如果本文能夠解答和幫助到你绪颖,使你能夠弄清楚你之前某一點的疑惑,也就夠了甜奄。