保護模式

介紹

當 80286 發明時,它支援傳統的 8086 分段(現在稱為真實模式),並新增了一種稱為保護模式的新模式。此模式已經在每個 x86 處理器中使用,儘管通過各種改進(例如 32 位和 64 位定址)進行了增強。

設計

在保護模式下,完全取消了簡單的將地址新增到移位段暫存器值。他們保留了 Segment 暫存器,但是他們不是使用它們來計算地址,而是使用它們索引到一個表(實際上是兩個……中的一個),它定義了要訪問的段。這個定義不僅描述了 Segment 在記憶體中的位置(使用 Base 和 Limit),還描述了它是什麼型別的 Segment(程式碼,資料,堆疊甚至系統)以及哪些程式可以訪問它(OS Kernel,正常程式) ,裝置驅動程式等)。

段暫存器

每個 16 位段暫存器採用以下形式:

+------------+-----+------+
| Desc Index | G/L | Priv |
+------------+-----+------+
 Desc Index = 13-bit index into a Descriptor Table (described below)
 G/L        = 1-bit flag for which Descriptor Table to Index: Global or Local
 Priv       = 2-bit field defining the Privilege level for access

全域性/本地

全域性/本地位定義訪問是進入全域性描述符表(不出所料地稱為全域性描述符表或 GDT),還是本地描述符表(LDT)。LDT 的想法是每個程式都有自己的描述符表 - 作業系統定義一組全域性段,每個程式都有自己的原生代碼,資料和堆疊段。作業系統將管理不同描述符表之間的記憶體。

描述表

每個描述符表(全域性或本地)是一個 64K 陣列,包含 8,192 個描述符:每個 8 位元組的記錄定義了它描述的段的多個方面。段暫存器的描述符索引欄位允許 8,192 個描述符:沒有巧合!

描述

描述符儲存了以下資訊 - 請注意,描述符的格式隨著新處理器的釋出而改變,但每個處理器都保留了相同的資訊:

  • Base
    這定義了記憶體段的起始地址。
  • 限制
    這定義了記憶體段的大小 - 排序。他們必須做出決定:0x0000 的大小是否意味著 0 的大小,所以無法訪問?還是最大尺寸?
    相反,他們選擇了第三個選項:限制欄位是細分中的最後一個可定址位置。這意味著可以定義一個單獨的段; 或者地址大小的最大大小。
  • 型別
    有多種型別的段:傳統的程式碼,資料和堆疊(見下文),但也定義了其他系統段:
    • 本地描述符表段定義了可以訪問的本地描述符的數量;
    • 任務狀態段可用於硬體管理的上下文切換;
    • 受控的呼叫門,可以允許程式呼叫作業系統 - 但只能通過精心管理的入口點。
  • 屬性
    在相關的情況下,還維護了段的某些屬性:
    • 只讀與讀寫;
    • Segment 當前是否存在 - 允許按需記憶體管理;
    • 什麼級別的程式碼(作業系統與驅動程式與程式)可以訪問此段。

真正的保護終於!

如果作業系統將段描述表保留在僅由程式無法訪問的段中,那麼它可以嚴格管理定義了哪些段,以及為每個段分配和訪問的記憶體。程式可以製造它喜歡的任何段暫存器值 - 但是如果它具有將其實際載入段暫存器中大膽性 !…… CPU 硬體將認識到所提出的描述符值破壞了大量規則中的任何一個,並且它不會完成請求,而是會向作業系統引發異常,以允許它處理錯誤的程式。 ** **

這個例外通常是#13,一般保護例外 - 以微軟 Windows 聞名世界……(任何人都認為英特爾工程師迷信?)

錯誤

可能發生的各種錯誤包括:

  • 如果建議的描述符索引大於表的大小;

  • 如果提議的描述符是系統描述符而不是程式碼,資料或堆疊;

  • 如果提議的描述符比請求程式更具特權;

  • 如果建議的描述符被標記為不可讀(例如程式碼段),但它被嘗試為 Read 而不是 Executed;

  • 如果提議的描述符標記為不存在。

    請注意,最後一個可能不是程式的致命問題:作業系統可以記下該標誌,恢復 Segment,將其標記為現在然後允許故障指令繼續成功。

或者,也許描述符已成功載入到段暫存器中,但隨後使用它進行訪問會破壞許多規則之一:

  • 段暫存器載入了 GDT 的 0x0000 描述符索引。這被硬體保留為 NULL;
  • 如果載入的描述符標記為只讀,但嘗試寫入它。
  • 如果訪問的任何部分(1,2,4 或更多位元組)超出了段的限制。