80386的分段機制、分頁機制和實體地址的形成
阿新 • • 發佈:2019-01-23
注:本分類下文章大多整理自《深入分析linux核心原始碼》一書,另有參考其他一些資料如《linux核心完全剖析》、《linux c 程式設計一站式學習》等,只是為了更好地理清系統程式設計和網路程式設計中的一些概念性問題,並沒有深入地閱讀分析原始碼,我也是草草翻過這本書,請有興趣的朋友自己參考相關資料。此書出版較早,分析的版本為2.4.16,故出現的一些概念可能跟最新版本核心不同。
MOVE REG,ADDR ; 它把地址為ADDR(假設為10000)的記憶體單元的內容複製到REG 中 在8086 的真實模式下,把某一段暫存器(段基址)左移4 位,然後與地址ADDR 相加後被直接送到內存總線上,這個相加後的地址(20位)就是記憶體單元的實體地址,而程式中的這個地址ADDR就叫邏輯地址
所謂描述符(Descriptor),就是描述段的屬性的一個8 位元組儲存單元。
一個段描述符指出了段的32 位基地址和20 位段界限(即段大小)。第6 個位元組的G 位是粒度位,當G=0 時,段長表示段格式的位元組長度,即一個段最長可達1M 位元組。當G=1 時,段長表示段的以4K 位元組為一頁的頁的數目,即一個段最長可達1M×4K=4G 位元組。D 位表示預設運算元的大小,如果D=0,運算元為16 位,如果D=1,運算元為32 位。 第7 位P 位(Present) 是存在位,表示段描述符描述的這個段是否在記憶體中,如果在記憶體中。P=1;如果不在記憶體中,P=0。 DPL(Descriptor Privilege Level)
3、系統段描述符
系統段描述符的第5 個位元組的第4 位為0,說明它是系統段描述符,型別佔4 位,沒有A 位。第6 個位元組的第6 位為0,說明系統段的長度是位元組粒度,所以,一個系統段的最大長度為1M 位元組。 系統段的型別為16 種,如圖2.15 所示。在這16 種類型中,保留型別和有關286 的型別不予考慮。門也是一種描述符,有呼叫門、任務門、中斷門和陷阱門4 種門描述符。
4、選擇符、描述符表和描述符表暫存器 描述符表(即段表)定義了386 系統的所有段的情況。所有的描述符表本身都佔據一個位元組為8 的倍數的儲存器空間,空間大小在8 個位元組(至少含一個描述符)到64K 位元組(至多含8K=8192)個描述符之間。 1.全域性描述符表(GDT) 全域性描述符表GDT(Global Descriptor Table),除了任務門,中斷門和陷阱門描述符外,包含著系統中所有任務都共用的那些段的描述符。它的第一個8 位元組位置沒有使用。 2.中斷描述符表(IDT) 中斷描述符表IDT(Interrupt Descriptor Table),包含256 個門描述符。IDT 中只能包含任務門、中斷門和陷阱門描述符,雖然IDT 表最長也可以為64K 位元組,但只能存取2K位元組以內的描述符,即256 個描述符,這個數字是為了和8086 保持相容。 3.區域性描述符表(LDT) 區域性描述符表LDT(Local Descriptor Table),包含了與一個給定任務有關的描述符,每個任務各自有一個的LDT。有了LDT,就可以使給定任務的程式碼、資料與別的任務相隔離。每一個任務的區域性描述符表LDT 本身也用一個描述符來表示,稱為LDT 描述符,它包含了有關區域性描述符表的資訊,被放在全域性描述符表GDT 中,使用LDTR進行索引。 在真實模式下,段暫存器儲存的是真實的段基址,在保護模式下,16 位的段暫存器無法放下32 位的段基址,因此,它們被稱為選擇符,即段暫存器的作用是用來選擇描述符。選擇符的結構如圖2.16 所示。
可以看出,選擇符有3 個域:第15~3 位這13 位是索引域,表示的資料為0~8129,用於指向全域性描述符表中相應的描述符。第2 位為選擇域,如果TI=1,就從區域性描述符表中選擇相應的描述符,如果TI=0,就從全域性描述符表中選擇描述符。第1、0 位是特權級,表示選擇符的特權級,被稱為請求者特權級RPL(Requestor Privilege Level)。只有請求者特權級RPL 高於(數字低於)或等於相應的描述符特權級DPL,描述符才能被存取,這就可以實現一定程度的保護。 下面講一下在沒有分頁操作時,定址一個儲存器運算元的步驟。 (1)在段選擇符中裝入16 位數,同時給出32 位地址偏移量(比如在ESI、EDI 中等)。 (2)先根據相應描述符表暫存器中的段地址(確定描述符表的地址)和段界限(確定描述符表的大小),根據段選擇符的TI決定從哪種描述符表中取,再根據段選擇符的索引找到相應段描述符的位置,比較RPL與DPL,若該段無問題,就取出相應的段描述符放入段描述符高速緩衝暫存器中。 (3)將段描述符中的32 位段基地址和放在ESI、EDI 等中的32 位有效地址相加,就形成了32 位實體地址。
5、linux中的段機制 從2.2 版開始,Linux 讓所有的程序(或叫任務)都使用相同的邏輯地址空間,因此就沒有必要使用區域性描述符表LDT。 Linux 在啟動的過程中設定了段暫存器的值和全域性描述符表GDT 的內容,段暫存器的定義在include/asm-i386/segment.h 中: C++ Code
1 2 3 4 5 |
#define __KERNEL_CS 0x10 //核心程式碼段,index=2,TI=0,RPL=0 #define __KERNEL_DS 0x18 //核心資料段, index=3,TI=0,RPL=0 #define __USER_CS 0x23 //使用者程式碼段, index=4,TI=0,RPL=3 #define __USER_DS 0x2B //使用者資料段, index=5,TI=0,RPL=3 |
1 2 3 4 5 6 7 8 9 10 |
ENTRY(gdt_table) .quad 0x0000000000000000 /* NULL descriptor */ .quad 0x0000000000000000 /* not used */ .quad 0x00cf9a000000ffff /* 0x10 kernel 4GB code at 0x00000000 */ .quad 0x00cf92000000ffff /* 0x18 kernel 4GB data at 0x00000000 */ .quad 0x00cffa000000ffff /* 0x23 user 4GB code at 0x00000000 */ .quad 0x00cff2000000ffff /* 0x2b user 4GB data at 0x00000000 */ .quad 0x0000000000000000 /* not used */ .quad 0x0000000000000000 /* not used */ |
• 第31~12 位是20 位頁表地址,由於頁表地址的低12 位總為0,所以用高20 位指出32 位頁表地址就可以了。 • 第0 位是存在位,如果P=1,表示頁表地址指向的該頁在記憶體中,如果P=0,表示不在記憶體中。 • 第1 位是讀/寫位,第2 位是使用者/管理員位,這兩位為頁目錄項提供硬體保護。當特權級為3 的程序要想訪問頁面時,需要通過頁保護檢查,而特權級為0 的程序就可以繞過頁保護,如圖2.23 所示。 • 第3 位是PWT(Page Write-Through)位,表示是否採用寫透方式,寫透方式就是既寫記憶體(RAM)也寫快取記憶體,該位為1 表示採用寫透方式。第4 位是PCD(Page Cache Disable)位,表示是否啟用快取記憶體,該位為1 表示啟用快取記憶體。 • 第5 位是訪問位,當對頁目錄項進行訪問時,A 位=1。 • 第7 位是Page Size 標誌,只適用於頁目錄項。如果置為1,頁目錄項指的是4MB 的頁面,即擴充套件分頁。 80386 的每個頁目錄項指向一個頁表,儲存在一個4K 位元組的頁面中,頁表最多含有1024 個頁面項,每項4 個位元組,包含頁面的起始地址和有關該頁面的資訊。頁面的起始地址也是4K 的整數倍,所以頁面的低12 位也留作它用,如圖2.24 所示。
第31~12 位是20 位物理頁面地址,除第6 位外第0~5 位及9~11 位的用途和頁目錄項一樣,第6 位是頁面項獨有的,當對涉及的頁面進行寫操作時,D 位被置1。
4GB 的儲存器只有一個頁目錄,它最多有1024 個頁目錄項,每個頁目錄項又含有1024個頁面項,因此,儲存器一共可以分成1024×1024=1M 個頁面。由於每個頁面為4K 個位元組,所以,儲存器的大小正好最多為4GB。 當訪問一個操作單元時,如何由分段結構確定的32 位線性地址通過分頁操作轉化成32位實體地址呢? 第一步,CR3 包含著頁目錄的起始地址,用32 位線性地址的最高10 位A31~A22 作為頁目錄表的頁目錄項的索引,將它乘以4,與CR3 中的頁目錄表的起始地址相加,形成相應頁目錄項的地址。 第二步,從指定的地址中取出32 位頁目錄項,它的低12 位為0,這32 位是頁表的起始地址。用32 位線性地址中的A21~A12 位作為頁表中的頁表項的索引,將它乘以4,與頁表的起始地址相加,形成相應頁表項的地址。 第三步,從指定地址中取出32位頁表項,它的低12位為0,這32位是頁面地址,將A11~A0 作為相對於頁面地址的偏移量,與32 位頁面地址相加,形成32 位實體地址。
8、linux 中的分頁機制 Linux 的分段機制使得所有的程序都使用相同的段暫存器值,這就使得記憶體管理變得簡單,也就是說,所有的程序都使用同樣的線性地址空間(0~4GB)。Linux 採用三級分頁模式而不是兩級。如圖2.28 所示為三級分頁模式,為此,Linux定義了3 種類型的表。 • 總目錄PGD(Page Global Directory) • 中間目錄PMD(Page Middle Derectory) • 頁表PT(Page Table)