任务和特权级保护(一)——《x86汇编语言:从实模式到保护模式》读书笔记27
本文及后面的幾篇文章是原書第14章的讀書筆記。
1.LDT(局部描述符表)
在之前的學習中,不管是內核程序還是用戶程序,我們都是把段描述符放在GDT中。但是,為了有效實施任務間的隔離,處理器建議每個任務都應該有自己的描述符表,稱為局部描述符表LDT(Local Descriptor Table),并且把專屬于這個任務的那些段描述符放到LDT中。
和GDT一樣,LDT也是用來存放段描述符的。不同之處在于,LDT只屬于某個任務。或者說,每個任務都有自己的LDT,每個任務私有的段,都應當在LDT中進行描述。
需要注意的是:LDT的0號槽位,是有效的,可以使用的。
GDT是全局性的,為所有的任務服務,是它們所共有的,我們只需要一個GDT就夠了。為了追蹤GDT,訪問它內部的描述符,處理器使用了GDTR寄存器。
和GDT不同,LDT的數量不止一個,具體有多少,根據任務的數量而定。為了追蹤和訪問這些LDT,處理器使用了LDTR(局部描述符表寄存器)。在一個多任務的系統中,會有很多任務輪流執行,正在執行的那個任務,稱為當前任務(Current Task)。因為LDTR只有一個,所以,它只用于指向當前任務的LDT。每當發生任務切換時,LDTR的內容被更新,以指向新任務的LDT。
當向段寄存器加載段選擇子的時候,段選擇子的TI(Bit2)位是表指示器(Table Indicator)。
TI=0:處理器從GDT中加載描述符
TI=1:處理器從LDT中加載描述符
因為描述符索引占用了13個比特,所以每個LDT最多能容納的描述符個數是8192(2的13次方),也就是說每個LDT只能定義8192個段;又因為每個段描述符占用8個字節,所以LDT的最大長度是64KB(2的16次方)。
2.TSS(任務狀態段)
在一個多任務環境中,當任務發生切換時,必須保存現場(比如通用寄存器,段寄存器,棧指針等)。為了保存被切換任務的狀態,并且在下次執行它時恢復現場,每個任務都應當有一片內存區域,專門用于保存現場信息,這就是任務狀態段(Task State Segment)。
TSS的格式如下圖所示:
TSS的最小尺寸是104(0x68=104)字節。
和LDT類似,處理器用任務寄存器TR(Task Register)指向當前任務的TSS。和GDTR、LDTR一樣,TR在處理器中也只有一個。當任務發生切換的時候,TR的內容會跟著指向新任務的TSS。這個過程是這樣的:首先,處理器將要掛起的任務的現場信息保存到TR指向的TSS;然后,使TR指向新任務的TSS,并從這個TSS中恢復現場。
下圖是我根據原書圖14-1繪制而成的,對我們理解GDTR、TR、LDTR和多任務的關系很有幫助。
3.全局空間和局部空間
每個任務實際上包括兩個部分:全部部分和私有部分。全局部分是所有任務共有的,含有操作系統的數據、庫程序、系統調用等;私有部分是每個任務自己的數據和代碼,與任務要實現的功能有關,彼此并不相同。
從內存的角度來看,所謂的全局部分和私有部分,其實是地址空間的劃分,即全局地址空間(簡稱全局空間)和局部地址空間(簡稱局部空間)。
對地址空間的訪問離不開分段機制,全局地址空間用GDT來指定,局部地址空間由每個任務私有的LDT來指定。
從程序員的角度看,任務的全局空間包含了操作系統的段,是由別人編寫的,但是他可以調用這些段的代碼,或者獲取這些段中的數據;任務的局部空間的內容是由程序員自己編寫的。通常,任務在自己的局部空間運行,當它需要操作系統提供的服務時,轉入全局空間執行。
4.特權級保護概述
在分段機制的基礎上,處理器引入了特權級的概念,并由固件負責實施特權級保護。
特權級(Privilege Level),是存在于描述符及其選擇子中的一個數值。當這些描述符或者選擇子所指向的對象要進行某種操作,或者被別的對象訪問時,該數值用于控制它們所能進行的操作,或者限制它們的可訪問性。
Intel處理器可以識別4個特權級別,分別是0~3,數值越小特權級越高。
如下圖所示(圖片來自維基百科):這是Intel處理器所提供的4級環狀結構。
通過,操作系統是為所有程序服務的,可靠性最高,而且必須對軟硬件有完全的控制權,所以它的主體部分必須擁有特權級0,處于整個環形結構的中心。因此,操作系統的主體部分通常被稱作內核(Kernel、Core)。
特權級1和2通常賦予那些可靠性不如內核的系統服務程序,比較典型的就是設備驅動程序。不過,在很多流行的操作系統中,驅動程序也是0特權級。
應用程序的可靠性被視為是最低的,而且通常不需要直接訪問硬件和一些敏感的系統資源,通過調用設備驅動程序和操作系統例程就能完成絕大多數工作,所以賦予它們最低的特權級別3.
5.CPL,DPL,RPL
想搞清楚段級保護,必須要弄懂這三個概念。
5.1.CPL
CPL:當前特權級(Current Privilege Level),存在于CS寄存器的低兩位。
當處理器正在一個代碼段中取指令和執行時,這個代碼段所在的特權級叫做當前特權級。正在執行的這個代碼段,其選擇子位于段寄存器CS中,CS中的低兩位就是當前特權級的數值。
一般來說,操作系統的代碼正在執行時,CPL就等于0;
相反,普通的應用程序則工作在特權級3上。應用程序的加載和執行,是由操作系統主導的,操作系統一定會將其放在特權級3上(具體的做法,我們會慢慢學到)。當應用程序開始執行時,CPL自然會是3.
需要注意的是,不能僵化地看待任務和任務的特權級別。當任務在自己的局部空間執行時,CPL等于3;當它通過調用系統服務,進入操作系統內核,在全局空間執行時,CPL就變成了0.(具體過程我們會在后面講解。)
5.2.DPL
DPL:描述符特權級(Descriptor Privilege Level),存在于段描述符中的DPL字段。
DPL是每個描述符都有的字段,故又稱描述符特權級。描述符總是指向它所描述的目標對象,代表著該對象。因此,DPL實際上是目標對象的特權級。
如果你忘了描述符的格式,可以看看下圖。
5.3.RPL
RPL:請求特權級(Requested Privilege Level),存在于段選擇子的低兩位。
要想將控制從一個代碼段轉移到另一個代碼段,通常是使用jmp或者call指令,并在指令中提供目標代碼段的選擇子和偏移;為了訪問內存中的數據,也必須先將段選擇子加載到段寄存器,比如DS、ES、FS、GS中。不管是實施控制轉移還是訪問數據段,這都可以看成是一個請求,請求者提供一個段選擇子,請求訪問指定的段。從這個意義上來說,RPL也就是指請求者的特權級別。
也許你會疑惑:有CPL和DPL進行判斷不就可以了嗎?為什么還需要一個RPL呢?
因為當低特權級的應用程序使用call far指令通過調用門將控制轉移到較高特權級的非一致代碼段(例如操作系統提供的例程,假設此代碼段的DPL=0)時,會改變當前的特權級,而在目標代碼段的特權級上執行,對于本例來說CPL的數值就會變成操作系統例程段的DPL的數值,即0。如果沒有RPL,那么此時CPL權限是最高的,也就可以去訪問任何數據,這就不安全了。所以引入RPL,讓它代表訪問權限,因此在檢查CPL的同時,也會檢查RPL.一般來說如果RPL的數值比CPL大(權限比CPL的低),那么RPL會起決定性作用。
6.IO特權級
在處理器的標志寄存器EFLAGS中,位12、13是IOPL位,也就是輸入/輸出特權級(I/O Privilege Level),它代表著當前任務的I/O特權級別。
如果CPL在數值上小于等于IOPL,那么所有的I/O操作都是允許的,針對任何硬件端口的訪問都可以通過。
相反,如果CPL的數值大于IOPL,也并不意味著所有的硬件端口都對當前任務關上了大門。事實上,處理器的意思是總體上不允許,但個別端口除外。至于是哪些個別端口,要找到當前任務的TSS,并檢索I/O許可位串(具體細節我們以后會說)。
只有當CPL=0時,程序才可以使用POPF或IRET指令修改這個字段。
總結
以上是生活随笔為你收集整理的任务和特权级保护(一)——《x86汇编语言:从实模式到保护模式》读书笔记27的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 输入两个长度相同的字符串,比较两个数在相
- 下一篇: python字符串类库_Python开发