【自制操作系統07】深入淺出特權級


一、到目前為止的程序流程圖

本講我們不繼續寫任何代碼,而是專門拿出一講來說說特權級的事,為后續的工作做一個知識儲備。這段內容太難啃了,也可能我恰好對這塊不太感冒,反正我是惡心了好久才啃下來。

為了讓大家清楚目前的程序進度,畫了到目前為止的程序流程圖,如下

二、什么時候處理器會進行特權級檢查

為什么要進行特權級檢查,我就不說太多了,簡單理解,操作系統不希望用戶進程訪問內核數據,所以需要給指令呀還有數據呀都附上一個特權級的屬性,讓程序受限制。

特權級分為 0 1 2 3 四種,我們常說的 用戶態 就是最低等級的 3 特權級,內核態 就是最高等級的 0 特權級。

處理器在 訪問數據跳轉到代碼 時,需要進行特權級檢查,特權級檢查的具體細則在下面有具體描述。這里我先把大致的思想方向總結出來:

  • 不論是訪問數據,還是跳轉到代碼,特權級檢查僅發生在 重新加載選擇子 時,而不是每條指令都檢查一遍。
  • 對於 訪問數據 來說,只能高特權級的指令訪問地特權級的數據
  • 對於 跳轉到代碼 來說,只能平級跳轉,如果想從低特權級跳到高特權級需要通過 ,如果想從高特權級跳到低特權級需要通過 返回指令

一些術語

  • CPL:處理器當前特權級
  • DPL:段或門的特權級,段描述符或者門描述符的 DPL 字段(45-46位)
  • RPL:請求特權級,選擇子 RPL 字段(0-1位)

特權級檢查的時機與條件

受訪者為數據時

  • 檢查時機:特權級檢查會發生在往 數據段寄存器 中加載 段選擇子 的時候,數據段寄存器包括 DS 和附加段寄存器 ES、FS、GS,如

mov ds,ax

  • 檢查條件CPL <= 目標數據段DPL && RPL <= 目標數據段DPL (只能高特權級的指令訪問地特權級的數據)

受訪者為代碼時

  • 檢查時機:特權級檢查會發生在能夠 改變 代碼 段寄存器 CS指令指針寄存器 EIP 的指令中,即這些指令要么改變 EIP,要么改變 CS 和 EIP。例如 call、jmp、int、ret、sysexit 等能改變程序執行流的指令,如

call 內核選擇子

  • 檢查條件
    • 無門結構且目標為非一致代碼段:CPL = RPL = 目標代碼段DPL
    • 無門結構且目標為一致代碼段:CPL >= 目標數據段DPL && RPL >= 目標數據段DPL
    • 有門結構:DPL_GATE >= CPL >= DPL_CODE && RPL <= DPL_GATE(從低特權級跳到高特權級需要通過門)

總結成最精煉的一句話就是:數據只能高的訪問低的,代碼只能從低的跳到高的(門或一致),從高到低只有返回指令可以完成

三、門描述符

門描述符一共有四種,分別是

  • 任務門描述符
  • 中斷門描述符
  • 跳轉門描述符
  • 調用門描述符

這些描述符也是記錄在 描述符表 中的,與之前說的 段描述符 一樣。所以這里把之前的段描述符,以及今天要說的四種門描述符,都畫在下面的圖中

type值 存在位置 用法
任務門 0101 GDT、LDT、IDT 與TSS配合實現任務切換,不過大多數操作系統都不這么玩
中斷門 1110 IDT 進入中斷后屏蔽中斷(eflags的IF位置0),linux利用此實現系統調用,int 0x80
陷阱門 1111 IDT 進入中斷后不屏蔽中斷
調用門 1100 GDT、LDT 用戶用call或jmp指令從用戶進程進入0特權級

四、調用門跳轉流程

門描述符的訪問流程是類似的,這里我們用 調用門 來舉例。

沒有門描述符的時候,我們用 jmp 指令指向一個普通的段描述符,經過一次拼接(段基址 + 偏移地址)就得到了邏輯地址。

調用門是用 jmp 或者 call 指令跳轉過去的,當指向一個調用門時,無非就是多一次拼接而已,最開始的 選擇子:偏移地址 中的 選擇子 用來定位一個門描述符,偏移地址 則被忽略了,如下圖。

五、調用門跳轉特權級檢查

直觀地說就是:當前特權級必須比門特權級高,又必須比最終要跳到的代碼段的特權級低

下面用一個調用門的具體例子梳理一下整個過程,由於書中的描述太精彩了,我看完之后對整個流程的理解又有了一大飛躍,所以我原封不動粘貼過來:

假設當前處理器正在 DPL 為 3 的代碼段上運行,即正在運行用戶程序,故處理器當前特權級 CPL 為 3。此時用戶進程想獲取安裝的物理內存大小,該數據存儲在操作系統的數據段中,該段 DPL 為 0。由於當前運行的是用戶程序,CPL 為 3,所以無法訪問 DPL 為 0 的數據段。於是它使用調用門向系統救助。調用門是操作系統安裝在全局描述符表 GDT 中的,為了讓用戶進程可以使用此調用門,操作系統將該調用門描述符的 DPL 設為 3。該調用門只需要一個參數,就是用戶程序用於存儲系統內存容量的緩沖區所在數據段的選擇子和偏移地址。調用門描述符中記錄的就是內核服務程序所在代碼段的選擇子及在代碼段內的偏移量。用戶進程用“call 調用門選擇子”的方式使用調用門,此調用門選擇子是由操作系統提供的,該選擇子的 RPL 為 3,此時如果用戶偽造一個調用門選擇子也沒用,因為此選擇子是用來索引門描述符的,並不用來指向緩沖區的選擇子,調用門選擇子中的高 13 位索引值必須要指向門描述符在 GDT 中的位置,選擇子中低 2 位的 RPL 偽造也沒意義,因為此時 CPL 為 3,是短板,以它為主。此時處理器便進行特權級檢查,CPL 為 3,RPL 為 3,門描述符 DPL 為 3,即數值上(CPL≤DPL && RPL≤DPL)成立,初步檢查通過。接下來還要再將 CPL 與門描述符中選擇子所對應的代碼段描述符 DPL 比較,這是調用門對應的內核服務程序的 DPL,為敘述方便將其記作 DPL_CODE。由於 DPL_CODE 是內核程序的特權級,所以DPL_CODE 為 0,CPL 為 3,即數值上滿足 CPL≥DPL_CODE,CPL 比目標特權級低,檢查通過,該用戶程序可以用調用門,於是處理器的當前特權級 CPL 的值用 DPL_CODE 代替,記錄在 CS.RPL 中,此時CPL 變為 0。接下來,處理器便以 0 特權級的身份開始執行該內核服務程序,由於該服務程序的參數是用戶提交的緩沖區所在的數據段的選擇子及偏移量,為避免用戶將緩沖區指向了內核的數據區,安全起見,在該內核服務程序中,操作系統將這個用戶所提交的選擇子的 RPL 變更為用戶進程的 CPL,也就是指向緩沖區所在段的選擇子的 RPL 變成了 3。前面說過,參數都是內核在 0 級棧中獲得的,雖然用戶進程將緩沖區的選擇子及偏移量壓在了 3 特權級棧中,但由於調用門的特權級變換,參數已經由處理器在固件一級上自動復制到 0 特權級棧中了。用戶的代碼段寄存器 CS 也在特權級發生變化時,由處理器自動壓入到 0 特權級棧中,所以操作系統需要的參數都可以在自己的 0 特權級棧中找到。用戶緩沖區的選擇子修改過后,接下來內核服務程序將用戶所需要的內存容量大小寫到這個選擇子和用戶提交的偏移量對應的緩沖區。如果用戶程序想搞破壞,所提交的這個緩沖區選擇子指向的目標段不是用戶進程自己的數據段,而是內核數據段或內核代碼段,由於目標段的 DPL 為 0,雖然此時已在內核中執行,CPL 為 0,但選擇子 RPL 已經被改為 3,數值上不滿足 CPL≤DPL && RPL≤DPL,往緩沖區中的寫入被拒絕,處理器引發異常。如果用戶程序提交的緩沖區選擇子確實指向用戶程序自己的數據段,DPL 則為 3,數值上滿足 CPL≤DPL && RPL≤DPL,往緩沖區中的寫入則會成功。如果中斷服務程序內部再有訪問內核自己內存段的操作,還會按照數值上(CPL≤DPL && RPL≤DPL)的策略進行新一輪的特權檢測。通常,如果不是用戶程序向內核提交緩沖區地址來接收數據的話,內核不會主動訪問用戶的內存段,多是訪問自己的數據段或代碼段,內核服務程序中若訪問內核自己的內存段,由於內存段的 DPL 為 0,所以段選擇子的 RPL 也必須為 0

六、總結

特權級檢查又是操作系統與處理器打配合的經典案例,處理器會在硬件層面做特權級檢查的工作,而操作系統負責在軟件層面定義特權級需要的相關數據(如選擇子和門描述符)

正常情況下代碼只能平級跳轉,除非是用門結構實現低跳高,或者返回指令實現高跳低。而數據只能是高特權級指令訪問低特權級的數據。

在內存中的數據和指令本沒有特權級的概念,本身也沒有訪問者或受訪者的概念。特權級被賦予在選擇子的 RPL 位,或者描述符的 DPL 位,配合着這兩個東西,指令和數據才有特權級的屬性,單獨的代碼和數據討論特權級是沒有意義的。這也順利成章地證明了處理器不會每執行一條指令就去檢查特權級,只是某些條件下才進行一次特權級檢查。

寫在最后:開源項目和課程規划

如果你對自制一個操作系統感興趣,不妨跟隨這個系列課程看下去,甚至加入我們(下方有公眾號和小助手微信),一起來開發。

參考書籍

《操作系統真相還原》這本書真的贊!強烈推薦

項目開源

項目開源地址:https://gitee.com/sunym1993/flashos

當你看到該文章時,代碼可能已經比文章中的又多寫了一些部分了。你可以通過提交記錄歷史來查看歷史的代碼,我會慢慢梳理提交歷史以及項目說明文檔,爭取給每一課都准備一個可執行的代碼。當然文章中的代碼也是全的,采用復制粘貼的方式也是完全可以的。

如果你有興趣加入這個自制操作系統的大軍,也可以在留言區留下您的聯系方式,或者在 gitee 私信我您的聯系方式。

課程規划

本課程打算出系列課程,我寫到哪覺得可以寫成一篇文章了就寫出來分享給大家,最終會完成一個功能全面的操作系統,我覺得這是最好的學習操作系統的方式了。所以中間遇到的各種坎也會寫進去,如果你能持續跟進,跟着我一塊寫,必然會有很好的收貨。即使沒有,交個朋友也是好的哈哈。

目前的系列包括


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM