讀書筆記:LINUX核心完全剖析:基於0.12核心
基本輸入/輸出程式BIOS 1981年IBM PC剛推出時系統只帶有640KB的RAM主儲存器,由於8088/8086CPU只有20根地址線,因此記憶體定址範圍最高為1024KB(1MB)。現在CPU的實體記憶體定址範圍已經高達4GB(甚至更高)。為了與原來的PC在軟體上相容, 系統1MB以下實體記憶體使用分配上仍然保持與原來PC基本一致
當計算機系統上電後,CPU會自動把 程式碼段暫存器CS設定為0xF000,其 段基地址被設定為0xFFFF0000,段長度設定為64KB(詳細解釋可見protected mode software architecture 第70頁)。而IP被設定為0xFFF0,因此CPU程式碼指標指向0xFFFFFFF0處(4GB空間最後16B)。BIOS會在這裡存放一條跳轉指令JMP,跳轉到BIOS程式碼中64KB範圍內某一條指令執行。由於目前PC/AT中BIOS容量大多有1MB-2MB,為了執行BIOS中超過64KB的其他BIOS程式碼或資料。BIOS程式會首先使用32位訪問方式把資料段暫存器的訪問範圍設定成4GB(而非原來的64KB)。這樣CPU就可以在0-4GB範圍執行和操作資料。此後,BIOS在執行一些硬體檢測和初始化操作後,就會把與原來PC相容64KB BIOS程式碼和資料複製到記憶體低端1MB末端的64KB處,然後跳轉到這個地方 讓CPU真正執行在實地址模式下(類似於8086執行方式 20根地址線)
保護模式和實地址模式: 關於實地址模式和保護模式:http://blog.csdn.net/heiworld/article/details/24371677 注意這裡面介紹的這句話: Intel選擇了在段暫存器的基礎上構築保護模式,並且保留段暫存器16位。 在保護模式下,它的段範圍不再受限於64K,可以達到4G。
分段機制: 80x86處理器使用了一種稱為 段的定址技術,從記憶體中定址需要使用段地址和段內偏移地址。段地址部分使用16位的段選擇符指定,段內偏移地址部分使用32位的值來指定。這兩部分構成48位地址稱為 邏輯地址
段選擇符是16位識別符號,用於提供段描述符表的偏移量,包含3個欄位:請求特權級RPL(位0、1)、表指示標誌TI(位2)、索引值(位3-15)。表索引欄位TI用來指出包含指定段描述符的段描述符表GDT或LDT。對應用程式來說段選擇符是作為指標變數的一部分而可見,但選擇符的值通常是由連結編輯器或連結載入程式進行設定或修改。由於段選擇符中有14位用於作為索引項,所以邏輯地址空間可包含最多16K(2^14)個段。每個段最長可達4GB(偏移地址32位)。段暫存器是用於存放段選擇符的暫存器。為了避免每次訪問記憶體時都去引用描述符表,段暫存器除去存放段選擇符部分,其餘的是描述符緩衝區(影子暫存器)。當一個段選擇符被載入到一個段暫存器可見部分時,處理器同時把段選擇符指向的段描述符中的段地址、段限長以及訪問控制資訊載入到段暫存器的影子暫存器區域。
段描述符表是段描述符的陣列。有兩種描述符表:全域性描述符表GDT和區域性描述符表LDT。虛擬空間被分割成大小相等的兩半,一半是由GDT對映的全域性虛擬地址空間,一半是由LDT對映的區域性虛擬地址空間。包含LDT表的段必須在GDT表中有一個段描述符項。而GDT本身並不是一個段,而是線性空間中的一個數據結構。訪問LDT需要使用其段選擇符。為了在訪問LDT時減少地址轉換次數,LDT的段選擇符、基地址、段限長以及訪問許可權需要存放在LDTR暫存器中。
全域性描述符表暫存器GDTR和區域性描述符表暫存器LDTR:GDTR暫存器用於存放全域性描述符表GDT的32位的線性基地址和16位的表限長值。基地址指定GDT表中位元組0線上性地址空間中的地址。GDT的限長以位元組位單位,因為段描述符總是8位元組長,所以GDT的限長值應該設定為總是8的倍數減1,GDT的基地址應該進行記憶體8位元組對齊。LDTR暫存器用於存放區域性描述符表LDT的32位線性基地址、16位限長和描述符屬性值以及段選擇符。
段描述符用於向處理器提供有關一個段的位置和大小資訊以及訪問控制的裝態資訊。每個段描述符的長度是8位元組,含3個主要欄位:段基地址、段限長和段屬性。段描述符通常由編譯器、連結器、載入器或者作業系統來建立。段型別欄位用行指定段或門的型別、說明段的訪問種類以及段的擴充套件方向。
把邏輯地址轉換成線性地址: (1)使用段選擇符中的偏移值在GDT或LDT表中定位相應的段描述符(僅當一個新的段選擇符載入到段暫存器中時才需要這一步)。 (2)利用段描述符檢驗段的訪問許可權和範圍,以確保該段是可訪問的並且偏移量位於段界限內。 (3)把段描述符中取得的段基地址加到偏移量上,最後形成一個線性地址。
使用LDTR暫存器中的存放的區域性描述符表線性基地址定位區域性描述符表LDT,通過段選擇符(TI = 1)來指定區域性描述符表的序列。
分段機制保護:
Conforming and Non-Conforming Code Segment
程式碼段可以是一致性的或者非一致性的。向更高特權級一致性程式碼段的執行控制轉移 (只要DPL <= CPL就可以轉移),允許程式以當前特權級CPL執行。向一個不同特權級(高於或低於)的非一致性程式碼段的轉移將導致一般保護異常(即DPL = CPL 才能轉移),除非使用了一個呼叫門或任務門。所有資料段都是非一致性的,即意味著它們不能被低特權級的程式或過程訪問,但可以被更高特權級的程式或過程訪問,而無需使用特殊的訪問門。
請求特權級RPL(Requested Privilege Level)處於段選擇符的1、0位 描述符特權級欄位DPL(Descriptor privilege level)處於段描述符的14、13位 當前特權級CPL(Current Privilege Level)處於CS和SS段暫存器的1、0位 protected mode software architecture中提法: the CPL(current privilege level) of the current program the RPL(requestor privilege level) in the segment register the DPL(descriptor privilege level) of the target code segment
the RPL represent the privilege level of the program that created the 16-bit value (the 16-bit value that is palced in the CS register during execution of a far jump or a far call instruction may have been created either by the program currently executing, or may have ben passed to it by another program as a parameter)。 RPL根據不同段跳轉確定,以動態重新整理CPL(比如 JMP 00D0:0003 中就指定了RPL為00)。
CPL是當前程序的許可權級別(Current Privilege Level),是當前正在執行的程式碼所在的段的特權級,存在於cs暫存器的低兩位。 (個人認為可以看成是段描述符未載入入CS前,該段的DPL,載入入CS後就存入CS的低兩位,所以叫做CPL,其值就等於原段DPL的值)
RPL說明的是程序對段訪問的請求許可權(Request Privilege Level),是對於段選擇符而言的,每個段選擇符有自己的RPL,它說明的是程序對段訪問的請求許可權,有點像函式引數。而且RPL對每個段來說不是固定的,兩次訪問同一段時的RPL可以不同。RPL可能會削弱CPL的作用,例如當前CPL=0的程序要訪問一個數據段,它把段選擇符中的RPL設為3,這樣雖然它對該段仍然只有特權為3的訪問許可權。(個人認為是以CPL來訪問段DPL所出示的“證件(RPL)”,如出示的“證件”權級範圍在CPL之內且滿足DPL的特權檢查規則:DPL >= max{CPL,RPL},就能正常通過DPL;反之則不會通過還會發生錯誤)
DPL儲存在段描述符中,規定訪問該段的許可權級別(Descriptor Privilege Level),每個段的DPL固定。當程序訪問一個段時,需要程序特權級檢查,一般要求DPL >= max {CPL, RPL}
普通轉跳(沒有經過Gate 這東西):即JMP或Call後跟著48位全指標(16位段選擇子+32位地址偏移),且其中的段選擇子指向程式碼段描述符,這樣的跳轉稱為直接(普通)跳轉。普通跳轉不能使特權級發生躍遷,即不會引起CPL的變化,看下面的詳細描述:
目標是一致程式碼段:
要求:CPL >= DPL ,RPL不檢查。
轉跳後程序的CPL = 轉跳前程式的CPL
目標是非一致程式碼段:
要求:CPL = DPL AND RPL<= DPL
轉跳後程序的CPL = 轉跳前程式的CPL
通過呼叫門的跳轉:當段間轉移指令JMP和段間轉移指令CALL後跟著的目標段選擇子指向一個呼叫門描述符時,該跳轉就是利用呼叫門的跳轉。這時如果選擇子後跟著32位的地址偏移,也不會被cpu使用,因為呼叫門描述符已經記錄了目的碼的偏移。使用調門進行的跳轉比普通跳轉多一個步驟,即在訪問呼叫門描述符時要將描述符當作一個數據段來檢查訪問許可權,要求指示呼叫門的選擇子的 RPL≤門描述符DPL,同時當前程式碼段CPL≤門描述符DPL,就如同訪問資料段一樣,要求訪問資料段的程式的CPL≤待訪問的資料段的DPL,同時選擇子的RPL≤待訪問的資料段或堆疊段的DPL。只有滿足了以上條件,CPU才會進一步從呼叫門描述符中讀取目的碼段的選擇子和地址偏移,進行下一步的操作。
從呼叫門中讀取到目的碼的段選擇子和地址偏移後,我們當前掌握的資訊又回到了先前,和普通跳轉站在了同一條起跑線上(普通跳轉一開始就得到了目的碼的段選擇子和地址偏移),有所不同的是,此時,CPU會將讀到的目的碼段選擇子中的RPL清0,即忽略了呼叫門中程式碼段選擇子的RPL的作用。完成這一步後,CPU開始對當前程式的CPL,目的碼段選擇子的RPL(事實上它被清0後總能滿足要求)以及由目的碼選擇子指示的目的碼段描述符中的DPL進行特權級檢查,並根據情況進行跳轉,具體情況如下:
目標是一致程式碼段:
要求:CPL >= DPL ,RPL不檢查,因為RPL被清0,所以事實上永遠滿足RPL <= DPL,這一點與普通跳轉一致,適用於JMP和CALL。
轉跳後程序的CPL = 轉跳前程式的CPL,因此特權級沒有發生躍遷。
目標是非一致程式碼段:
當用JMP指令跳轉時:
要求:CPL = DPL (RPL被清0,不檢查),若不滿足要求則程式引起異常。
轉跳後程序的CPL = DPL
因為前提是CPL=DPL,所以轉跳後程序的CPL = DPL不會改變CPL的值,特權級也沒有發生變化。如果訪問時不滿足前提CPL=DPL,則引發異常。
當用CALL指令跳轉時:
要求:CPL >= DPL(RPL被清0,不檢查),若不滿足要求則程式引起異常。
轉跳後程序的CPL = DPL
當條件CPL=DPL時,程式跳轉後CPL=DPL,特權級不發生躍遷;當CPL>DPL時,程式跳轉後CPL=DPL,特權級發生躍遷,這是我們當目前位置唯一見到的使程式當前執行優先順序(CPL)發生變化的跳轉方法,即用CALL指令+呼叫門方式跳轉,且目的碼段是非一致程式碼段。
http://blog.csdn.net/feijj2002_/article/details/4597174 http://blog.csdn.net/csujiangyu/article/details/46531931分頁機制:
分頁機制
80x86使用4K(2^12)位元組固定大小的頁面,對齊於4K地址邊界處。分頁機制把4GB(2^32)的線性地址和實體地址空間劃分成2^20個頁面,通過管理線性地址和實體地址對映關係實現分頁(線性地址的低12位可作為頁內偏移量直接作為實體地址的低12位)。 如果包含線性地址的頁面當前不在實體記憶體中,處理器就會產生一個頁錯誤異常,處理器把用於線性地址轉換成實體地址時所需的資訊及處理器產生錯誤異常所需的資訊儲存於頁目錄和頁表中。為了減少地址轉換所要求的匯流排週期數量,最近訪問的頁目錄和頁表會被存放在處理器的緩衝器件(轉換查詢緩衝區TLB)中。 CR3含有存放頁目錄表頁面的實體地址。頁目錄表頁面是頁對齊的,所以該暫存器只有高20位是有效的,在往CR3中載入新值時低12位必須設定為0。使用MOV指令載入CR3時,會讓TLB無效。80x86處理器並沒有維護頁轉換快取記憶體和頁表中資料的相關性,但是需要作業系統軟體來確保它們一致,因此作業系統必須在改動過頁表以後重新整理高速緩衝以確保兩者一致。通過簡單重新載入暫存器CR3,就可對快取記憶體器的重新整理操作。
頁表用於記錄2^20個頁面的物理基地址(20位)和相關屬性(12位),每項佔4位元組。
兩級頁表結構
實體地址的轉換分成兩步進行,每步轉換其中10bit 第一級稱為頁目錄(存放於1個4K頁面),具有2^10個4B的表項,線性地址的最高10位用於索引第二級(頁表),故需要2^10(1K)個頁表。 第二級稱為頁表(存放於1個4K頁面), 具有2^10個4B的表項,線性地址的中間10位用於獲取頁面20位物理基地址。 部分二級頁表存放於磁碟上,頁目錄中的目錄項有一個存在屬性用於指明二級頁表是否存在
分頁機制保護: 讀寫標誌R/W和使用者/超級使用者標誌U/S提供分頁機制保護。特權級0,1,2為超級使用者級,特權級3為普通使用者級 0-1MB記憶體空間用於核心系統(其實核心指使用0-640KB,剩下的部分被高速緩衝和裝置記憶體佔用)