1. 程式人生 > >引入TSS與堆疊切換

引入TSS與堆疊切換

【0】README

text description from orange’s implemention of a os and for complete code ,please visit https://github.com/pacosonTang/Orange-s-OS/blob/master/p62.asm.


【1】 回憶——關於堆疊

通過呼叫門進行有特權級變換的轉移——理論篇

  • (1)出現的問題: call 指令 執行前後的堆疊已經不再是同一個堆疊 了,那麼我們在堆疊A中壓入引數和返回地址, 需要出棧(ret or retf)時,堆疊卻變成了堆疊B, 這該怎麼辦呢?
  • (2)解決方法: Intel提供一種機制, 將堆疊A的內容複製到 堆疊B中, 如下圖;
    這裡寫圖片描述
    這裡寫圖片描述

  • (3)TSS閃亮登場(task-state segment -任務狀態段)
    (不同特權級的程式碼段間的轉移,會發生堆疊切換,使得呼叫者的入棧的堆疊是針對呼叫者本身的堆疊, 而出棧操作是針對被呼叫者的堆疊,即入棧和出棧的堆疊不一致,使得特權級間跳轉出錯,故引入了 TSS)

  • 出現的問題: 由於每個任務可能在4個特權級間轉移,故每個任務實際上需要4個堆疊;問題是:我們只有一個ss 和 esp, 那麼當發生堆疊切換,我們該從哪裡獲取其他堆疊的ss 和 esp 呢?

  • 解決方法: 我們引入TSS, 它可以解決這個問題, TSS 資料結構 和 TSS段描述符的資料結構 如下;
    這裡寫圖片描述

    這裡寫圖片描述

【2】舉個荔枝:

如,我們當前在ring3 , 當轉移只ring1 時, 堆疊將被自動切換到由 ss1 和 esp1 指定的位置。由於只是在外層到內層(低特權級到高特權級)切換時,新堆疊才會從TSS中取得,所以TSS 並沒有位於最外層 ring3 的堆疊資訊;

(2.1)轉移的過程中,CPU所做的工作:

  • 1) 根據目的碼段的DPL,從TSS中選擇應該切換到哪個ss 和 esp;
  • 2) 從TSS 中讀取新的ss 和 esp。在這個過程中,若發現ss、esp 或者 TSS 界限錯誤都會導致無效 TSS異常;
  • 3) 對ss 描述符進行檢驗,若發生錯誤,同樣產生#TS異常;
  • 4) 暫時性儲存當前ss 和 esp 的值;
  • 5) 載入新的 ss 和 esp;
  • 6) 將剛剛儲存起來的ss 和 esp 的值壓棧;
  • 7) 從呼叫者堆疊中將引數 複製到被呼叫者堆疊中(新堆疊中), 複製引數的數目由呼叫門中 Param Count一項來決定;
  • 8) 將當前的 cs 和 eip 壓棧;
  • 9) 載入呼叫門中指定的新的cs 和 eip, 開始執行被呼叫者過程;

(2.2)從被呼叫者到呼叫者的返回過程中, 處理器的工作:
(實際上,ret這個指令不僅可以實現短返回和長返回, 而且可以實現帶有特權級變換的長返回)

  • 1)檢查儲存的cs 中的RPL 以判斷返回時是否要變換特權級;
  • 2)載入被呼叫者堆疊上的cs 和eip;
  • 3)如果ret 指令含有引數,則增加esp 跳過引數,然後esp 將指向被儲存過的呼叫者ss 和 esp ;ret的引數個數對應 呼叫門中的 Param Count的值;
  • 4)載入ss 和 esp , 切換到呼叫者堆疊,被呼叫者的ss 和 esp 被丟棄;
  • 5)如果ret 指令含有引數, 增加esp 的值以跳過引數;
  • 6)檢查ds、es、fs、gs的值,如果其中哪一個暫存器指向的段的DPL 小於CPL(此規則不適用於一致程式碼段),那麼一個空描述符會被載入到該暫存器;

【3】總結:(使用呼叫門的過程實際上分為兩部分)

  • (1)從低特權級到高特權級,通過呼叫門和call 指令來實現;
  • (2)從高特權級到低特權級, 通過ret 指令來實現;(即,ret 指令可以實現從高特權級到低特權級的轉移)

(Attention):

  • A1)從呼叫者堆疊中將引數複製到被呼叫者堆疊(新堆疊)中, 複製引數的數目由 呼叫門中 Param Count 一項來決定,(呼叫門中 Param Count的作用);
  • A2) ret(retf)是call 的反過程, 只是帶引數的ret 指令會同時釋放事先被壓棧的引數;

【0】README