PA4

寫在前面的話

如果您對該系列感興趣的話,推薦您先看一下南京大學的計算機組成原理實驗(也就是PA)的講義,然後再來看這篇文章可能有更多地收穫。如果您是要完成該作業的學生,我推薦你先看講義,或者好好聽老師的講課,然後自己獨立完成這個作業,但是如果你沒有聽懂,或者你無論如何也無法理解講義上面的字,又或者說對講義上面的某點知識某個問題不瞭解而又覺得太簡單不好意思問老師,那麼您可能會從這篇文章裏面獲得一些你需要的信息。本篇文章將會包括筆者自己做PA的所有經過,希望你並不將該文章當成抄襲的根源,而是成爲你思考的源泉。
現在已經到了PA4的階段,經過了PA0的搭建環境、PA1簡單的功能函數實現、PA2的機器指令的模擬實現,PA3運行仙劍奇俠傳的運行,我們需要把NEMU改造地更加像一個真實的操作系統。
我們已經實現了所有的PA3任務,而PA4要做的事情更加底層,有很多內核、操作系統的知識,需要很好地瞭解之後再進行實現。

PA系列傳送門

PA0:https://blog.csdn.net/qq_41983842/article/details/88921427
PA1.1:https://blog.csdn.net/qq_41983842/article/details/88934779
PA1.2:https://blog.csdn.net/qq_41983842/article/details/89714479
PA1.3:https://blog.csdn.net/qq_41983842/article/details/89714689
PA2.1:https://blog.csdn.net/qq_41983842/article/details/95232055
PA2.2&2.3:https://blog.csdn.net/qq_41983842/article/details/101164495
PA3.1:https://blog.csdn.net/qq_41983842/article/details/103094859
PA3.2:https://blog.csdn.net/qq_41983842/article/details/103843093
PA4:https://blog.csdn.net/qq_41983842/article/details/104667951

目錄

思考題

  1. 如果發生了中斷嵌套, 將會發生什麼樣的災難性後果? 這一災難性的後果將會以什麼樣的形式表現出來?

    如果選擇把現場信息保存在一個固定的地方, 發生中斷嵌套的時候, 第一次中斷保存的現場信息將會被優先級高的中斷處理過程所覆蓋,那麼如果現場信息被保存在0x1000這個地址處,trap frame的信息就會被覆蓋,那麼等到嵌套的中斷結束的時候,中斷現場信息已經沒有了,所以程序就一直執行這個中斷處理過程,會造成死機,由於是硬件中斷,導致沒有任何除了拔電源的方式來阻止這個中斷處理過程結束。

  2. 解釋分頁機制和硬件中斷是如何支撐仙劍奇俠傳和 hello 程序在我們的計算機系統(Nanos-lite, AM, NEMU)中分時運行的

    首先分頁機制保證程序在虛擬空間上面運行,hello和仙劍的虛擬空間地址不一樣,所以他們不會相互衝突,這是能一起運行他們兩個的基礎,不然如果他們兩個地址有重合的地方運行其中一個肯定會把另外一個覆蓋的。有了這個保證,我們就可以通過硬件中斷來實現兩個程序分時運行。一開始現在仙劍的虛擬空間上面跑,然後跑着跑着,遇到了這個時鐘中斷指令,這個時候把現場保存下來,同時初始化好陷阱幀,程序跳轉到hello的虛擬地址空間來執行,之後又遇到中斷,繼續保存現場,然後構造仙劍的陷阱幀,CPU響應到了這個中斷信號,就通過_EVENT_IRQ_TIME這個事件來跳轉到schedule函數中切換進程。總的來數就是一個保存現場然後不斷進程切換的過程。

實驗內容

實現內核自陷

首先要實現_umake()函數,他的任務就是在 ustack 的底部初始化一個以 entry 爲返回地址的陷阱幀,也就是在棧上初始化如下內容, 然後返回陷阱幀的指針,並且要在陷阱幀之前設置好 _start() 函數的棧幀

|               |
+---------------+ <---- ustack.end
|  stack frame  |
|   of _start() |
+---------------+
|               |
|   trap frame  |
|               |
+---------------+ <--+
|               |    |
|               |    |
|               |    |
|               |    |
+---------------+    |
|       tf      | ---+
+---------------+ <---- ustack.start
|               |

有了上面這個圖,思路也變得清晰了,首先要把stack frame of _start()中參數全部設爲0或者NULL,然後就來到了trap frame的位置,開始初始化陷阱幀,就跟PA3.1的順序一樣來初始化他們就可以了。陷阱幀裏面每個元素都是int大小,也就是4個字節,所以4個字節4個字節減少指針或者把他們放在數組裏通過下標的變化來分別對應每個元素給他們賦初值都行。這裏我採用指針的方式。
在這裏插入圖片描述
然後要修改Nanos-lite的代碼,加入_trap();,並且在proc.c裏面做如下更改:
在這裏插入圖片描述
然後修改asye.c文件中的irq_handle函數加入0x81也就是自陷的case:
在這裏插入圖片描述
接着在_asye_init函數中根據system call來添加system trap中斷門的情況,查看x86.h中type參數有三種TG,IG32,TG32,顯然這裏是IG32,而entry就是我們下面要寫的函數,我聲明爲void systrap();
在這裏插入圖片描述
在這裏插入圖片描述
之後就要來到trap.s中來添加systrap這種entry情況。模仿前面的vecnullvecsys,可以很輕鬆寫出來這個操作。
在這裏插入圖片描述
_trap中,模仿框架中寫的,這裏要顯示中斷指令
在這裏插入圖片描述
這樣我們的操作系統就可以接收到一個 _EVENT_TRAP 事件,現在需要在Nanos-lite 接收到 _EVENT_TRAP 之後可以輸出一句話, 然後直接返回即可。需要在irq.c中添加相關case
在這裏插入圖片描述
這時候我們再運行一下dummy,就會發現這樣的情況:
在這裏插入圖片描述

實現上下文切換

這個任務主要是完成schedule() 函數來返回將要調度的進程的上下文。講義上面已經給出了基本的框架,通過 current 來決定接下來要調度哪一個進程,並且把當前進程的上下文信息的位置保存在 PCB 當中。新地址空間放在PCB的as中,而新的上下文放在tf指向的地方。
在這裏插入圖片描述
之後要修改剛纔改過的irp.c中的case,不讓他輸出話了,而是返回這個函數的返回值。爲了等會運行可以看出來是走的這個case,還是暫時輸出一下把。
在這裏插入圖片描述
要在common.h中聲明schedule函數

_RegSet* schedule(_RegSet *prev);

最後需要在trap.s裏面對asm_trap進行相應的修改,irq_handle() 返回後,先將棧頂指針切換到新進程的陷阱幀, 然後才根據陷阱幀的內容恢復現場。把 addl $4, %esp去除掉,把esp換到eax上面,就完成了切換
在這裏插入圖片描述
成功跑起來仙劍
在這裏插入圖片描述

分時運行仙劍奇俠傳和hello程序

添加第二個用戶程序
在這裏插入圖片描述
修改調度的代碼, 讓 schedule() 輪流返回仙劍奇俠傳和 hello 的現場
在這裏插入圖片描述
然後修改 do_event() 的代碼, 在處理完系統調用之後, 調用 schedule() 函數並返回其現場,也就是嵌套調用,先調用do_syscall然後用do_syscall的返回值來賦給schedule進行進程調換。要注意do_syscall的返回值不能再是NULL了,不然怎麼對NULL進行進程切換呢?
在這裏插入圖片描述
這分時運行可是把我的仙劍給卡炸了,幾秒鐘刷新一個畫面
在這裏插入圖片描述

優先級調度

爲了解決剛纔哪個問題,就要在schedule函數裏面下功夫了,之前是每次調用這個函數,都會在hello和仙劍之間切換一次,而這次想做到每調用200次仙劍,切換一次hello輸出一下,這樣我們的仙劍就跑的快了。解決方法就是加一個count變量來記錄進程切換的次數,每調用一次schedule計數器加一,當加滿200的時候,跳到hello執行一下,然後把計數器清零,重新開始計時。
在這裏插入圖片描述
在這裏插入圖片描述
這下子我的仙劍跑起來相比剛纔就流暢太多了。
在這裏插入圖片描述

添加時鐘中斷

這裏要實現一個類似於硬件中斷的機制。首先在 CPU 結構體中添加一個 bool 成員 INTR
在這裏插入圖片描述
dev_raise_intr() 中將INTR 引腳設置爲高電平.
在這裏插入圖片描述
exec_wrapper() 的末尾添加輪詢 INTR引腳的代碼, 每次執行完一條指令就查看是否有硬件中斷到來
在這裏插入圖片描述
修改 raise_intr() 中的代碼, 在保存 EFLAGS 寄存器後, 將其 IF 位置爲 0, 讓處理器進入關中斷狀態.
在這裏插入圖片描述
asye.c 中添加時鐘中斷的支持, 將時鐘中斷打包成 _EVENT_IRQ_TIME 事件。和之前的trap事件一樣。經過百度查找到x86的時鐘中斷處理程序是int32 -- (int 0x20) 中斷頻率被設置爲100Hz(include/linux/sched.h,5),這樣就可以在irq_handle()函數裏面添加時鐘中斷的event:
在這裏插入圖片描述
然後在下面的_asye_init中添加時鐘中斷idt索引,在這裏entry聲明爲systime,其他的跟之前的idt沒有任何區別。
在這裏插入圖片描述
在這裏插入圖片描述
接下來要做的就是在trap.s中來寫這個systime函數,跟前面的三個函數基本上一模一樣,區別的地方就在於irqid0x20
在這裏插入圖片描述
這樣就能打包成爲_EVENT_IRQ_TIME事件。接下來要在do_event函數中識別出來這個事件,添加一個case,直接調用 schedule() 進行進程調度,輸出一句話證明是時鐘中斷,並且去掉系統調用之後調用的 schedule() 代碼
在這裏插入圖片描述
修改 _umake() 的代碼, 在構造現場的時候, 設置正確的EFLAGS。這裏所說的正確的eflags其實指的就是IF位,之前我們在保存eflags寄存器的時候設置了IF位爲0,就是關中斷,此時即使 INTR 引腳爲高電平, CPU 也不會響應中斷.所以我們在構造現場的時候要把IF位設爲1,開中斷狀態,這樣CPU就會處理我們的時鐘中斷信號了。
在這裏插入圖片描述
跑一下仙劍,可以看出觸發了時鐘中斷事件
在這裏插入圖片描述

走過的一些彎路

  1. 在做4.1實現上下文切換的時候,找不到asm_trap在哪個地方

    認識不夠全面,始終以爲asm_trap是一個現成的c語言函數,在asye.c文件裏面怎麼也找不到,後來仔細思考了一下,看到了自己在trap.s中定義systrap的代碼,然後往下一看就發現了asm_trap這個關鍵詞,PA3的時候根本不知道trap.s是幹啥的,也看不懂,現在要修改這個文件,總算知道一點東西了。比對講義,也明白了asm_trap的功能,對之前的pushapopa也有了更進一步的理解。

  2. 在實現上下文切換之後,更改了schedule函數之後,在運行仙劍的時候仍然無法切換到hello程序,看不到輸出

    沒有在do_event中更改syscall的這種case,導致一直都在系統調用,但是沒有根據每次的系統調用來傳遞到進程切換這個函數,所以最終得等到_EVENT_TRAP這種case的時候纔會調用schedule函數,這就導致了他只會進行一次進程切換,更改之後要在每次系統調用的時候都要傳遞參數rschedule函數中。

PA4到此就基本上結束了,而整個PA系列也要告一段落了,非常感謝大家的閱讀,願不久之後再相見!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章