【 聲明:版權所有,歡迎轉載,請勿用於商業用途。 聯繫信箱: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通信這些基本功能就行了。