oranges 筆記第六章

OS 第六次實驗隨筆

第六章6.1-6.3相關的問題

  1. 進程狀態保存與恢復數組

    • 哪些狀態
    • 什麼時候保存
    • 保存在哪
    • 如何恢復
  2. 特權級變換數據結構

    • 用戶進程到內核
    • 內核回到用戶進程
  3. 再次理解TSS 、堆棧函數

    • 從外環進入內環(特權級發生變化)時,如何訪問TSS?堆棧的變化指針

    • P193頁,中斷髮生的開始,ESP的值是剛剛從TSS裏面渠道的進程表A中regs的最高地址,上述過程的實現代碼在哪?rest

概述

  1. 造成進程的必要考慮。咱們須要一數據結構來記錄一個進程的狀態,這樣方便進行進程切換時,能夠保存原來的進程狀態。進程被掛起時就將信息寫入這個數據結構,進程從新啓動時,這裏的信息就被從新讀取。進程

    image-20201119203312062

  2. 因爲進程和進程調度運行在不一樣層級上,這裏爲了簡單,讓全部任務運行在ring1,讓進程切換運行在ring0。ip

簡單的進程

一個簡單的進程切換情形,一個進程在執行時,發生了時鐘中斷,特權級從ring1跳轉到ring0,開始執行時鐘中斷處理程序,中斷處理程序這時調用進程調度模塊,指定下一個應該運行的進程,當中斷處理程序結束時,下一個進程準備就緒並開始運行,特權級又從ring0跳轉回ring1:內存

  1. 進程A運行中;
  2. 時鐘中斷髮生,ring1->ring0,時鐘中斷處理程序啓動;
  3. 進程調度,下一個應運行的進程(這裏設爲進程B)被指定;
  4. 進程B被恢復,ring0 -> ring1;
  5. 進程B運行中。
  6. image-20201126181428931

爲了實現這種進程切換,須要如下模塊:it

  • 時鐘中斷處理程序;
  • 進程調度模塊;
  • 兩個進程。

關鍵技術

進程的哪些狀態須要保存?

須要保存的是那些可能會發生改變的量,從寄存器和內存兩個角度考慮,由於不一樣進程之間的內存是互不干涉的,可是CPU卻只有一個,不一樣的進程共用同一個CPU的一套寄存器,因此應該保存寄存器的值,準備進程被恢復時使用。基礎

保存的是處理機狀態信息,包括

a. 進程當前暫存信息;
b. 下一條指令地址信息;
c. 進程狀態信息;
d. 過程和系統調用參數及調用地址信息.

進程的狀態什麼時候以及怎樣保存?

爲了保證進程狀態完整不被破壞,在進程剛剛掛起時保存全部寄存器的值。保存的方法爲push(即存到PCB中),或者是push ad.

這些代碼應該寫在時鐘中斷例程的最頂端,以便中斷髮生時立刻被執行。

如何恢復?

保存使用的是push,恢復用的即是pop,將寄存器的值都恢復,而後執行指令iretd,回到對應進程。

進程表的引入

保存進程狀態的東西,即進程表,亦或進程控制塊(PCB)。

因爲會有對個進程,因此會有多個進程表,造成一個進程表數組。

進程表是描述進程的,必須獨立於進程以外。

進程棧和內核棧

  1. 進程運行時,esp指向進程棧的某一塊;
  2. 中斷髮生,從ring1到ring0,esp的值變爲TSS中預設的ring0下的esp的值;
  3. 寄存器的值剛被保存到進程表中,這時候再進行堆棧操做會破壞進程表數組;
  4. 這時候使用內核棧,使esp指向內核棧便可。

特權級變換

  1. 發生有特權級變換的轉移時,若是是外層向內層轉移,咱們須要從TSS中取得從當前TSS中取出內層SS和esp做爲目標代碼的ss和esp,因此每一個進程都要有TSS,涉及的描述符應該放在局部描述符表LDT中,因此腰圍每一個進程準備LDT。
  2. 利用iretd來實現ring0 到ring1的轉換。

ring0 -> ring1

restart

分析代碼

358行:將esp更改,即讓esp指向進程表,p_proc_ready指針指向的是下一個要啓動進程的進程表地址。

359行:設置ldt,esp+p_ldt_sel即指向進程表中的ldt_sel,可知在執行restart以前對ldt_sel進行了初始化

360、361:將進程表的第一個成員的regs末地址賦值給TSS中的ring0堆棧指針域(esp)。下一次執行中斷時。esp將變成regs的末地址。

時鐘中斷處理程序

這裏直接使用iretd

進程表,進程體,GDT和TSS

  1. 須要初始化的寄存器有:cs ds es fs gs ss esp eip eflags
  2. 要初始化LDT
  3. 初始化TSS
  4. 相關關係
    • 進程表和GDT。進程表內的LDT Selector對應GDT中的一個描述符,而這個描述符所指向的內存空間就存在於進程表內。
    • . 進程表和進程。進程表是進程的描述,進程運行過程當中若是被中斷,各個寄存器的值都會被保存進進程表中。可是,在 咱們的第一個進程開始以前,並不須要初始化太多內容,只須要知道進程的入口地址就足夠了。另外,因爲程序免不了 用到堆棧,而堆棧是不受程序自己控制的,因此還須要事先指定esp。
    • GDT和TSS。GDT中須要有一個描述符來對應TSS,須要事先初始化這個描述符
  5. 初始化一個進程體
  6. 初始化進程表
    • 定義進程表的結構體。
    • 初始化寄存器
    • 初始化LDT和LDT Selector
  7. 準備GDT和TSS。

豐富中斷處理程序

時鐘中斷起做用

  1. 首先在i8259.c的init8259a中打開中斷。

    image-20201202115003521

  2. 設置EOI使中斷持續不停的發生

    image-20201202115154500

  3. 爲了使中斷能夠被觀察,這裏經過改變屏幕第0行,第0列的字符來表示中斷在運行。中斷髮生時會一直變成下一個

    image-20201202115423652

  4. 運行結果以下所示,此時截圖到了6

  5. image-20201202151339328

現場保護與恢復

爲何不用disp_str,而是直接使用move寫顯存?

這是由於disp_str會影響不少寄存器,進而可能致使對進程的影響。上面的改變al從結果上時沒有產生改變的但爲了不沒必要要的錯誤,咱們仍是將全部的寄存器都壓入堆棧,執行以後再恢復。

賦值 TSS.esp0

  1. 中斷的發生伴隨着頻繁的特權級切換,特權級切換除了要有代碼的跳轉,還須要堆棧的切換。
  2. 當從ring1回到ring0時,堆棧切換就須要TSS,TSS是用來存儲ring0的堆棧信息的,堆棧信息是ss和esp。
  3. 進程運行時,tss.esp0應該是當前進程的進程表中保存寄存器值的地方,即s_proc中的s_stackframe的最高地址處。
  4. 實現代碼分析
    • 首先是跳過四個字節,即跳過retaddr,獲得了進程表A中的regs的最高地址。
    • 而後是保存寄存器的值image-20201202142809942
    • 使ds,es,指向與ss相同的段
    • 接下來是同上面的屏幕顯示相關代碼;
    • 最後將esp+p_stacktop的值給eax,而後賦值給tss.esp0。下一次執行中斷時,esp將指向regs的末地址;
    • image-20201202143936335
    • 而後將寄存器出棧,esp+4

內核棧

前面咱們知道,進程調度發生時,僅僅切換到進程表是不夠的,爲了防止進程表相應的棧的有關信息被破壞,咱們還要繼續切換棧,這時候就要用到內核棧了。

切換到內核棧的方法很是簡單,只需在原代碼的基礎上增長兩行move便可,切換的時機爲寄存器剛被壓入堆棧之後(壓入堆棧後就當即跳轉到內核棧,不然此時進行堆棧操做會影響進程表存儲的信息),以及對tss.esp0賦值以前(不然下次中斷就直接內核棧了)。

image-20201202144727122image-20201202144744056

這裏增長了中斷顯示消息"",因此會在下面顯示「」,以下圖所示

image-20201202113543663

中斷重入

爲了讓中斷髮生時,還能繼續接受中斷,咱們須要進行必定的修改。

  1. CPU在響應中斷時,會自動的關中斷,這是咱們要人爲打開,因此要使用指令sti打開中斷,並在離開內核棧前用cli關閉中斷;

    image-20201202155437980image-20201202160623849

  2. 爲了實驗的方便,咱們須要中斷處理的時間足夠長,因此增長了延時函數

    image-20201202155528301

  3. 這樣會帶來一個問題,即當一次中斷未完成時,另外一箇中斷就發生,致使永遠沒法執行到中斷處理程序的結尾,做用於顯示上就是打印一個A0x0後一直打印^。

    image-20201202162818716

  4. 而且因爲一直在壓棧而無出棧,會致使堆棧溢出。

  5. 解決這個問題,須要讓中斷處理程序知道本身是否在嵌套執行。設置一個全局變量來實現,中斷處理程序執行時自加,結束時自減。這裏設置爲初始值爲-1.當結果不爲0時,說明未處理完髮生了中斷,這時直接跳到最後,結束新的中斷。

  6. image-20201202163112043

  7. 運行結果以下,可知打印^的速度變慢了,說明不少程序執行了inc byte後並無執行disp_str。中斷重入已經解決,因此無需延時函數,最後運行結果如最下方所示

    image-20201202163547100

  8. image-20201202164008209

問題

取到進程表A中regs的最高地址的代碼

實現的代碼其實沒有,是CPU本身實現的

實驗

修改系統調用函數

image-20201202172701861

將proc.c中的對應函數改成上述,即調用後指向下一個進程,若是進程爲最後一個,返回到進程表的第一個。

修改進程體

在a的地方,進行調用後面加入第二句話。在C處使用延時函數。

image-20201202173023927

相關文章
相關標籤/搜索