好多概念, 花了好長時間看懂, 過一段時間就忘得一乾二淨,等要用到時,又得從零開始看起, 實在是很浪費時間。
所以把自己剛剛看懂的東西小結一下, 以備後用。 觀點不成熟, 看的時候小心點,別被誤導了。
1. 實模式和保護模式段的差別。
實模式和保護模式下都使用16位的段寄存器存放段基址信息,如CS,SS,ES等。不同的是, 實模式下段寄存器中存放的就是段基址,使用時把段基址*16 加上16位的偏移量就得到20位的地址信息。而在保護模式下, 所有段定義在一張表中,也就是我們常說的GDT,每個表項以8個字節詳細定義某個段的起始地址、界限、屬性等內容,而段寄存器中存放的段值此時只是起到一個索引的作用, 它指向GDT中的某個表項, 這個索引也就是我們常說的段選擇子。使用的時候根據索引取得32位的段基址和普通寄存器中32位的段偏移,兩者共同組成了實際的物理地址。
2. jmp指令
有幾種常見形式:
A. jmp xx, 跳轉至某個標號, 跳轉目標地址在當前段內。 IP的計算方法是當前IP + (跳轉標號偏移-當前標號偏移)
實模式下偏移和保護模式的偏移概念是不一樣的, 但因爲IP的計算方法使得在實模式和保護模式下都能成功跳轉至目標標號。
B. jmp xx:xx,跳轉至某個段內某偏移, 前一個xx表示目標段, 後一個表示目標偏移。
3. $$ 表示當前節(section)開始的地址。
4. 描述符基本結構: 段基址(32位), 段界限(20位), 段屬性。通過一個8字節的描述符,詳細定義了一個段的相關信息。
5. 調用門結構: 指向目標段的GDT selector(16位),目標段偏移(32位),屬性(裏面包括param count, TYPE, DPL等)。這所有信息也是通過一個描述符來表示的, 並放在GDT中。
6. 特權級轉移
通常代碼裏的跳轉都是在段內進行的,但有時需要進行段間轉移。 先假設當前段DPL爲x。
不通過調用門,而直接通過jmp或者call跳轉時, 有以下規則:
一致代碼段: CPL>=x, 可以訪問,且跳轉後CPL不變。
非一致代碼段, CPL=x,且RPL<=x
可以看到,直接跳轉是非常有限的,要想自由跳轉,還需要通過調用門。
通過調用門,訪問規則如下(設調用門的DPL爲DPL_G):
一致代碼段: CPL<=DPL_G, RPL<=DPL_G, CPL>=x
非一致代碼段: 通過call指令時: CPL<=DPL_G, RPL<=DPL_G, CPL>=x, 通過jmp指令時: CPL<=DPL_G, RPL<=DPL_G, CPL=x
通過調用門,可以很好的實現低特權級向高特權級跳轉。
同時需要注意: 在通過調用門跳轉時,需要通過TSS保留從0~2的SS和EIP
7. CPL,RPL,DPL
《自己動手寫操作系統》第二版49頁寫得很清楚。
8. 分頁機制
一個內存地址的大小是32位, 低12位存放頁內偏移(也即1頁有4k大小), 中間10位指示該內存地址在哪一頁(一個頁目錄有1024頁),高10位指示處於那個頁目錄(總共有1024個頁目錄)。
PDE存放的是頁目錄表, 它最多有1024項, 每一項指向每個頁目錄的起始地址。
PTE存放的是頁表, 它最多有1024*1024項, 每一項指向某個頁的物理地址。
分頁通過PDE-PTE兩級映射。 Cr3存放頁目錄基地址。
9. 中斷和異常
中斷通常在程序執行時因爲硬件而隨機發生, 通常用來處理cpu外部的事件, 比如外圍設備的請求。
異常則通常在cpu執行指令過程中檢測到錯誤時發生,比如遇到零除的情況。
當它們發生時, 都需要跳轉到相應的處理程序中做相應處理, 對於中斷我們通過中斷向量來表示。
在實模式中的中斷向量具體是怎樣? 這個待研究。
在保護模式中, 首先需建立硬件中斷與向量號之間的對應關係, 如鍵盤中斷對應IRQ0, 鼠標中斷對應IRQ1等。
中斷向量通過IDT進行描述,和GDT類似, 它通過lidt命令加載IDT基址。 這個IDT可以理解爲一個數組,其中的每個元素就叫做IDT描述符,描述符的基本結構是目標選擇子, 目標偏移, 屬性。 IDT[0]就表示IRQ0,IDT[1]表示IRQ1, 依此類推, 形成所謂的中斷向量。