隨想錄(386cpu保護模式)

【 聲明:版權所有,歡迎轉載,請勿用於商業用途。  聯繫信箱:feixiaoxing @163.com】

 

    寫過操作系統的同學都知道,編寫os除了基礎的操作系統理論之外,最大的工作就是需要閱讀cpu手冊。注意,這裏提到的是cpu手冊,不是soc手冊。比如說,s3c2416是三星的芯片,大家拿到的一般是這款芯片的soc手冊,但是如果需要查找arm的相關信息,還是應該去arm的官網看arm7、arm9的相關手冊。學習linux也是一樣,最早期的os都是在386完成的,所以對於pc底層開發的人員來說,386怎麼學習都不過。

 

1、386工作模式

386有兩種工作模式,一種是實模式,訪問地址空間只有1M;另外一種是保護模式,訪問空間有4G。

 

2、訪問地址的方法有什麼不同

實模式下,地址就是段寄存器 << 4 + 偏移地址

保護模式下,地址就是GDT+ 段寄存器基地址 +  Page分頁 + MMU表

一般GDT表中的前四個描述符write後不能修改

 

3、GDT、LDT、IDT是什麼

GDT是一張表,每個選項8個字節,一般用lgdt載入,輸入爲一個基地址,它存儲了GDT表的基地址和長度,共48位

LDT就是GDT裏面的一個選項,一般用lldt載入,輸入爲一個段寄存器索引

IDT也是一張表,每個表也是8個字節,一般用lidt載入,輸入爲一個基地址,它存儲了IDT表的基地址和長度,也是48位

注意:如果是實模式,那麼中斷向量表是在0x0的位置

void init()
{
	unsigned char gdt_base[6];
	unsigned char idt_base[6];

	__asm {
		lgdt gdt_base
		lidt idt_base
		mov ax, 0x10
		lldt ax
		sgdt gdt_base
		sidt idt_base
	}
}

 

4、段寄存器索引

在保護模式下,cs、ds、ss、es、fs、gs退化成一個索引。但是由於索引低3位保存了屬性,所以如果需要檢查段描述符的數值,可以直接GDT基地址 + 段寄存器來完成。同時,段寄存器也記錄了當前訪問的是LDT,還是GDT。

 

5、段寄存器的修改

cs外的寄存器都可以通過類似mov es/gs/ds/fs/ss ax這種方式來賦值

而cs寄存器只能通過jmp來完成,比如可以用es賦值給cs,start賦值給ip

	__asm {
	start:
		jmp es:start
	}

 

6、其他寄存器

386下面有dr0、dr1、dr2、dr3、dr4等調試寄存器,對於理解軟件調試很有幫助

除此之外,還有mmx、sse等多媒體指令,主要用於性能加速

	__asm {
		mov eax, dr0
		mov eax, dr1
		mov eax, dr2
		mov eax, dr3
	}

 

7、CALL門

作爲GDT表的一個段描述符存入,用段寄存器訪問,但是裏面的屬性、內容不同

CALL門會使用到兩個段描述符,這是稍微一點不同的地方

CALL門和jump使用廣泛,不同的優先級別會導致不同的壓棧方法,需要注意下

這種門描述符,一般不涉及具體的地址,它需要和一個段選擇符綁定在一起。

 

8、TSS和任務門

用一塊內存記錄TSS下的寄存器佈局內容(全局變量就可以),接着用一個GDT段描述符記錄這塊空間,最後用ltr指令 + 段寄存器進行加載。TSS和多任務綁定在一起,任務的切換,會導致GDT/LDT和TR寄存器的更新。一般GDT負責整個系統的佈局,LDT負責線程空間的分佈,而TR就是每個線程的上下文。TSS表示一段空間保存寄存器數據,而任務門表示一個任務狀態。每個任務可以都有一個屬於自己的TSS。

	__asm {
		mov eax, 0x10
		ltr eax
	}

jump到LDT中的任務門或者GDT中的某個TSS都會產生上下文切換。TSS中的地址也會保存在TCB信息頭當中。所有的操作都是硬件幫忙完成的,軟件做的其實就是切換了一下tr寄存器。

 

9、CPL、RPL、DPL

CPL表示當前段寄存器的優先級,一般特指cs中的優先級

RPL表示擬使用的優先級,表示cs外其他段寄存器的優先級

DPL表示段描述符的優先級,表示GDT表中描述符的優先級

 

10、如何進入保護模式

開啓A20 + cr0 pe位置起

 

11、如何分頁

開啓cr0 pg位 + 設置cr3寄存器,即MMU的目錄首地址

在此之前,應該準備好表目錄和頁目錄,可以用GDT的描述符生成,一個段描述符對應一個目錄

初始化好就可以

MMU裏面的表目錄、頁目錄屬性特別多,用到的時候學就可以了。

保護模式默認是分段模式,分頁模式開啓後,cpu會再做一次MMU的映射工作,這就是它的基本原理。

	__asm {
		mov eax, cr0
		mov cr0, eax
		mov eax, cr3
		mov cr3, eax
	}

 

12、如何eflags置位

	__asm {
		pushfd
		pop eax
		or eax,0x1
		push eax
		popfd
	}

 

13、輸入輸出

in、out指令

	__asm {
		mov dx, 0x1234
		in ax, dx
		out dx, ax
	}

 

14、系統調用

int 15/ int 21等等,通過系統調用可以讓cpu從用戶模式跳入內核模式

	__asm {
		int 10
		int 15
		int 21
	}

15、tlb和cache問題

查找對應彙編指令解決,參考IA32 Architecture Developer‘s Manual II

還有一種方法就是參考linux 386 cpu抑制部分的代碼

 

16、參考資料有哪些

IA32 Architecture Developer‘s Manual I,II, III

<自己動手寫操作系統>中第二章

<linux 0.11完全解析>

<X86 彙編語言 從實模式到保護模式>

 

17、主要的分析工具

qemu + gcc + binutils 等工具

 

PS:

    從上面的描述也知道,數據空間 + 段描述符 + 段寄存器是保護模式學習的重中之重。理解了這個,其他概念都可以迎刃而解。當然,有了概念的理解還是不夠的,最好利用qemu實踐一下,或者找一些開源代碼,比如ucore,實際操作一把,效果更好。

 

    對於操作系統來說,OS不一定會使用到全部cpu特性。即使像linux這樣大的系統,他們使用到的也只是一個通用的cpu特性,只要滿足指令集、寄存器、輸入輸出、異常、中斷、MMU、cache、多 core通信這些基本功能就行了。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章