在看文章前可以先看下這個,

吳海波:專欄的序。

先有個大概的認識會對閱讀有所幫助。

在正式的介紹硬體提供的分段,分頁,任務管理,中斷異常處理,高速緩存控制這些內容之前,我們先對IA-32的整體框架梳理下。看不懂也沒有關係,後面每篇文章都會對其中的一部分進行較為細緻的分析。

整體來說IA-32架構的可以提供如下幾塊功能:

1.內存管理;

2.軟體模塊的保護;

3.多任務處理;

4.異常和中斷處理;

5.多核;

6.緩存管理;

7.硬體資源和電源管理;

8.調試和性能監視。

我們這裡主要關注前面6塊的內容。因為我們的關注點在於面向cpu的編程,那麼首先就需要知道IA-32架構的cpu提供給我們什麼樣的編程環境,換句話說,我們能夠操作什麼硬體單元來完成我們想要完成的操作?(比如算數和邏輯運算,接受外部輸入產生輸出,管理外部的硬體等等)。

一:對系統架構的概述

下面的圖2-1描述了IA-32提供給我們的一個基本執行環境中包含的硬體單元。

文檔中將這些寄存器進行了簡單的介紹。名詞翻譯為中文會有些怪,最好能記住英文名稱和簡寫。

在前面os章節中我們說過幾個地址的概念,再重新提下。首先就是邏輯地址(logical address),邏輯地址就是段基址加上段內偏移,將邏輯地址合成以後的地址稱為線性地址(linear address)。如果沒有分頁,線性地址就是最終的物理地址,而如果有分頁的存在,則還需要經過分頁機制的轉換才能得到最終的物理地址。

1.全局和局部描述符表(Global and local Descriptor tables)

如果你想訪問內存,那麼你肯定要通過全局描述符表(global descriptor table,GDT)或者局部描述符表(local descriptor table , LDT)中的一個來獲取內存的信息。GDT和LDT都是簡單的數組類型的數據結構,這2個表中的信息稱為段描述符(segment descriptor)。段描述符中提供段的基地址,段的大小,訪問控制,類型等等信息,通過這些你就可以定位到具體的內存區域了。

每個段描述符都可以通過段選擇子(segment selector)來訪問。你可以將段選擇子當作段描述符的名字,其中包括該段描述符的類型(全局還是局部),許可權,對應的GDT或者LDT表中的索引(有了這個索引就可以定位到具體的段描述符了)。

若要訪問段中的位元組,必須提供段選擇子和偏移量。通過段選擇子確定了段描述符後,從段描述符中,處理器獲得段的線性地址空間的基地址。然後,偏移量提供相對於基地址的偏移位置。這個機制可用於訪問任何有效的代碼、數據或堆棧段,條件是該段可從處理器的當前許可權級別(CPL)訪問。CPL就是當前正在執行的代碼段的許可權級別(總共有4個許可權級別,分別為0,1,2,3,數字越大許可權越小)。

GDT,LDT這2個數組結構的數據都放在內存的某個地方,至於是哪裡你可以自己來定。intel提供了2個寄存器來存儲對應的線性基地址,分別為GDTR,LDTR。最後那個R就是寄存器register的縮寫。

2.系統段,段描述符,門(System Segments, Segment Descriptors, and Gates )

除了構成程序或過程執行環境的代碼、數據和堆棧段之外,處理器的體系結構還定義了兩個類型的系統段:任務狀態段(task-state segment,TSS)和LDT。GDT不被認為是段,因為它不是通過段選擇子和段描述符訪問的(從上面你可以知道,訪問GDT是通過GDTR中的GDT基地址進行訪問的)。而TSS和LDT有為它們定義的段描述符,可以通過段選擇子找到段描述符來訪問。

另外,處理器架構還定義了一種特別類型的描述符叫做門(gate)。gate提供了一種機制,就是可以跨許可權級別的訪問。比如一個call命令,如果當前的許可權是3(cpu的等級分為4個,分別是0,1,2,3,其中數字越大,許可權越小),要想訪問許可權定義為0的代碼,那麼就可以通過call命令加上對應的門描述符來完成。關於gate的信息,後面會詳細展開來說。這塊的內容還是很多的。

3.任務狀態段和任務門(Task-State Segments and Task Gates)

TSS中會定義一個任務的執行環境的信息。具體來說包括如下部分:通用寄存器,段寄存器,EFLAGS寄存器,EIP寄存器,棧段對應的段選擇子(一個cpu的許可權必須對應一個不同的段,也就是說你如果是一個許可權等級為3的程序,你會有4個棧,分別對應0,1,2,3這幾個許可權等級)。TSS還會包括該任務的LDT的段選擇子和分頁數據結構的信息。

TSS作為一個系統段(如第二點所說),訪問它就需要段選擇子,這個段選擇子保存在任務寄存器TS(task regeiter)中。一個簡單的執行任務切換的方式就是通過CALL或者JMP命令加上一個TSS的段選擇子來完成。下面是切換任務的時候,處理器完成的幾個步驟:

1.將當前任務的狀態存儲在對應的TSS中。

2.將新任務的段選擇子載入到TR中。

3.通過GDT中的段描述符訪問新的TSS。

4.將新任務的狀態從新的TSS載入到通用寄存器、段寄存器、LDTR、控制寄存器CR3(分頁層次結構的基地址)、EFLAGS寄存器和EIP寄存器中。

5.開始執行新任務。

4.中斷和異常處理

外部中斷、軟體中斷和異常通過中斷描述符表(IDT)處理(這也是一個數組的結構,類似GDT和LDT)。IDT存儲一組門描述符,提供對中斷和異常處理程序的訪問。和GDT一樣,IDT也不是一個段。IDT線性基地址存儲在IDT寄存器(IDTR)中。IDT中的門描述符類型可以是中斷(interrupt)、陷阱(trap)或任務門描述符(TSS)。處理器可以從內部硬體、外部中斷控制器或通過int、INT 3或bound等指令接收中斷向量。中斷向量就是一個索引,根據這個索引就可以對IDT這個數組類型的數據結構進行訪問了。如果所選的門描述符是中斷門或陷阱門,對應處理程序的訪問類似於通過一個調用門(call gate)來訪問一個過程。如果描述符是任務門,則通過任務的上下文切換來訪問(就像上面第三點的那五個步驟所示)。

5.內存管理

內存管理就2種情況,要不分段,要不分段+分頁。也就是說分段是關閉不掉的功能,分頁是程序,也就是os來指定是否需要開啟的。

6.系統寄存器(System Registers)

剩下的寄存器後面再來說。

二:處理器支持的運行模式

我們先來看下IA-32支持哪幾種運行的模式:

1.保護模式(Protected mode)。這個是處理器原生支持的模式,在這種模式下工作,將能夠運用處理器提供的非常豐富並且高性能的特性,以及對以前處理器的兼容性。你用電腦的99.99%情況下處理器都是工作在這種模式下的。

2.系統管理模式(System management mode (SMM))。主要供os進行電源管理和OEM差異功能的實現。

3.實模式(Real-address mode)。這個是開機或者重啟機器一開始的時候處理器的模式,這種模式只能跑intel8086處理器對應的程序,我們要說的現代操作系統需要的功能大部分都是沒有的。

4.虛擬8086模式(Virtual-8086 mode)。就是能在保護模式下跑8086的程序。

Intel64架構支持IA-32的所有模式和IA-32e模式,IA-32e簡單來說就是64位對32位的兼容模式。

三:EFLAGS 寄存器

EFLAGS寄存器如下所示,這個寄存器裡面有很多的控制位,根據這些控制位會有很多不同的系統工作方式,我們在後面使用到其中內容的時候再介紹。

四:內存管理寄存器

處理器提供了四個內存管理的寄存器(GDTR, LDTR, IDTR, and TR)來指定內存段的地址信息。

五:控制寄存器(contral registers)

控制寄存器有5個,CRO,CR1,CR2,CR3,CR4.下面的圖2.7詳細的展示了各個控制寄存器的內容。這些寄存器包含的含義我們後面用到的時候再介紹,主要先有個印象,控制寄存器嘛,肯定是控制什麼東西的。

六:Protection Key Right Register(PKRU)(我就不翻譯這個名字了。。。)

如果CPUID.(EAX=07H,ECX=0H):ECX.PKU [bit 3] = 1,這句的意思是執行CPUID指令,輸入是這個(EAX=07H,ECX=0H),輸出是ECX,在ECX的第三個bit位,如果是1,表示處理器支持4級頁表下的protection-key特性。這個特性簡單的來說就是對頁表可以支持更為細緻的許可權保護。PKRU如下面的圖2-9展示的這樣。

PKRU包括16個控制對,每個對i都包含2個bit位:

Bit 2i:在圖中標記為AD的:如果設置了該位,處理器就會禁止普通用戶模式下對該線性地址的訪問。

Bit 2i+1:圖中標記為WD,如果設置,就是禁止普通用戶模式對該線性地址的寫操作。

七:系統指令概要(System instruction)

系統指令是用來實現系統層面功能的,比如載入系統寄存器,管理緩存,管理中斷,或者設置debug寄存器。這種類型的指令很多都只有在許可權為0的情況下才能執行,也有一些是可以在任意的許可權級別上執行。下面的表2-3給出了系統指令的執行許可權的情況:

對於前面提到的GDTR,LDTR,IDTR,TR這些寄存器,可以使用下面的指令來載入和修改:

? LGDT (Load GDTR Register) —從內存中載入GDT的基地址和大小到GDTR中

? SGDT (Store GDTR Register) —把GDTR中的基地址和大小保存保存到內存中

? LIDT (Load IDTR Register) —從內存中載入IDT的基地址和大小到IDTR中。

? SIDT (Store IDTR Register)—把IDTR中的基地址和大小保存保存到內存中。

? LLDT (Load LDTR Register) —載入LDT的段選擇子和段描述符到LDTR中,段選擇子和段描述符的數據是事先保存在內存中的(也可以從通用寄存器載入)。

? SLDT (Store LDTR Register) —保存LDT的段選擇子到一個通用寄存器中。

? LTR (Load Task Register) —載入TSS的段選擇子和段描述符,段選擇子和段描述符的數據是事先保存在內存中的(也可以從通用寄存器載入)。

? STR (Store Task Register) —保存TR寄存器中的當前TSS選擇子到內存中或者通用寄存器中

LMSW(load machine status word)SMSW(store machine status word)可以操作CRO寄存器的0到15位,這是為了保持和Intel286保持兼容而設置的。對於IA-32的處理器不一樣再使用這個指令,相應的應該使用MOV CR指令。

對於控制寄存器,應該使用MOV指令進行操作。通過MOV指令可以載入或者保存控制寄存器,相應的目的操作數是通用寄存器。

對於Debug的支持提供了8個debug 寄存器(DR0-DR7)。MOV指令可以設置和保存這些存儲器的值。

存儲器提供了一些指令用來顯示的將緩存或者TLB置失效。INVD(invalidate cache with no writeback)指令會將內部緩存的所有數據和指令都置為時效,然後給外部緩存發送信息,讓它們同樣將緩存置為失效。

WBINVD(invalidate cache with writeback)指令的功能和INVD一樣,除了會在置失效之前會將已經被修改過的內部緩存的數據寫會到主存中。本地內存置為失效後,WBINVD會發送信號,來將在指令執行的過程中被修改的數據寫會和置為失效。

需要注意的是,非共享的緩存不會寫會也不會置為失效。下面的圖2-10中,如果運行WBINVD指令是在LP0或者LP1,那麼LPO/LP1的L1和L2緩存會寫回並且置為失效,L3也會如此。但是其他的logic processor的L1和L2緩存並不會寫會或者置為失效。

INVLPG(invalidate TLB entry)指令會將TLB中的指定頁緩存置為失效。


推薦閱讀:
相關文章