lab3——進程和異常


思考題

Thinking 3.1

為什么我們在構造空閑進程鏈表時必須使用特定的插入的順序?(順序或者逆序)

為了保證鏈表中Env塊的順序和在envs中的順序相同

Thinking 3.2

思考env.c/mkenvid 函數和envid2env 函數:

• 請你談談對mkenvid 函數中生成id 的運算的理解,為什么這么做?

• 為什么envid2env 中需要判斷e->env_id != envid 的情況?如果沒有這步判斷會發生什么情況?
習\操作系統課設\

低10位用於表示當前Env在envs中的位置,高位則表示調用分配函數的次數。

如果只有低位的話,當Env被重復調用的時候id就會重復,而只有高位的話則沒法根據envid查找對應Env在envs中的位置。

如果沒有判斷e->env_id!=envid的話,可能實際上這一進程並沒有分配id,但是因為未分配時低10位是0而被誤判為envs中第一個進程。

Thinking 3.3

結合include/mmu.h 中的地址空間布局,思考env_setup_vm 函數:

• 我們在初始化新進程的地址空間時為什么不把整個地址空間的pgdir 都清零,而是復制內核的boot_pgdir作為一部分模板?(提示:mips 虛擬空間布局)

• UTOP 和ULIM 的含義分別是什么,在UTOP 到ULIM 的區域與其他用戶區相比有什么最大的區別?

• 在env_setup_vm 函數的最后,我們為什么要讓pgdir[PDX(UVPT)]=env_cr3?(提示: 結合系統自映射機制)

• 談談自己對進程中物理地址和虛擬地址的理解

因為我們采用的是2G/2G的布局,沒有真正的內核進程,每個用戶進程都可能臨時變為內核態從而獲得內核空間的管理權限。所以有可能會需要用boot_pgdir訪問相應的內核區域

UTOP含義是用戶可以使用的空間中的最高地址,ULIM含義是用戶空間的最高地址(再往上就是內核空間了)

它們之間的區域應該是用戶沒有權限修改的

因為env_cr3儲存的是進程頁目錄的物理地址,通過這樣的賦值完成了頁目錄的自映射。

進程中的虛擬地址就是對於每個用戶來說獨立的”假地址”,物理地址是共同使用的真實地址。

Thinking 3.4 思考user_data 這個參數的作用。沒有這個參數可不可以?為什么?(如果你能說明哪些應用場景中可能會應用這種設計就更好了。可以舉一個實際的庫中的例子)

不可以。因為需要這個參數傳遞的信息才可以完成加載二進制鏡像的作用,如果沒有的話得不到頁目錄的信息。

Thinking 3.5 結合load_icode_mapper 的參數以及二進制鏡像的大小,考慮該函數可能會面臨哪幾種復制的情況?你是否都考慮到了? (提示:1、頁面大小是多少;2、回顧lab1中的ELF文件解析,什么時候需要自動填充.bss段)

  1. bin_size<BY2PG-offset (填入的文件內容不能充滿一頁中的剩余位置)

  2. bin_size>=BY2PG-offset && bin_size-BY2PG+offset %BY2PG !=0 (可以充滿剩余位置,但是文件內容末尾不是BY2PG的整數倍)

  3. bin_size>=BY2PG-offset && bin_size-BY2PG+offset %BY2PG =0 (可以充滿且是整數倍)

sg_size>bin_size的時候需要自動填充

這里的e->env_tf.pc是什么呢?就是在我們計組中反復強調的甚為重要的PC。它指示着進程當前指令所處的位置。你應該知道,馮諾依曼體系結構的一大特點就是:程序預存儲,計算機自動執行。我們要運行的進程的代碼段預先被載入到了entry_ point為起點的內存中,當我們運行進程時,CPU 將自動從pc 所指的位置開始執行二進制碼。

Thinking 3.6 思考上面這一段話,並根據自己在lab2 中的理解,回答:

• 我們這里出現的” 指令位置” 的概念,你認為該概念是針對虛擬空間,還是物理內存所定義的呢?

• 你覺得entry_point其值對於每個進程是否一樣?該如何理解這種統一或不同?

是針對虛擬空間定義的,指令所在的物理頁面可能是分散的,但是虛擬地址是連續的,所有只有按照虛擬地址才能順序執行指令。

entry_point對於每個進程來說是相同的,都是從elf文件中讀取的,不過他們儲存的物理地址是不同的。

Thinking 3.7 思考一下,要保存的進程上下文中的env_tf.pc的值應該設置為多少?為什么要這樣設置?

應該設為epc的值,因為這樣可以在處理完中斷之后返回到之前的位置繼續執行

Thinking 3.8 思考TIMESTACK 的含義,並找出相關語句與證明來回答以下關於TIMESTACK 的問題:

• 請給出一個你認為合適的TIMESTACK 的定義

• 請為你的定義在實驗中找出合適的代碼段作為證據(請對代碼段進行分析)

• 思考TIMESTACK 和第18 行的KERNEL_SP 的含義有何不同

TIMESTACK是內核中保存現場所用的棧指針,以下sizeof(struct Trapframe)保存了當前進程的上下文信息

 SAVE_ALL
1:
    move k0, sp
    get_sp
    move k1, sp
    subu sp, k1, TF_SIZE

 get_sp
    mfc0 k1, CP0_CAUSE
    andi k1, 0x107C
    xori k1, 0x1000
    bnez k1, 1f
    nop
    li sp, 0x82000000
    j 2f
    nop
1:
    bltz sp, 2f
    nop
    lw sp, KERNEL_SP
    nop
2:
    nop
.endm

get_sp先判斷是否發生的是4號中斷引起的異常,然后決定該用哪個棧指針。如果是的話把sp設為TIMESTACK用於保存上下文

TIMESTACK是產生時鍾中斷異常時用的棧指針,KERNEL_SP是非時鍾中斷異常用的棧指針

Thinking 3.9 閱讀 kclock_asm.S 文件並說出每行匯編代碼的作用

將1存入0xb5000100的位置開啟實時鍾,sp的值存入內核棧,調用宏來設置CP0的狀態,返回上一級函數

Thinking 3.10 閱讀相關代碼,思考操作系統是怎么根據時鍾周期切換進程的。

當時鍾中斷產生時,MIPS將PC指向異常處理代碼段,調用handle_int函數處理

handle_int讀取CPU_CASUE和CPU_SATUS到t0t2中,把t0 and t2存入t0得到具體的中斷號,如果判斷是4號中段位則執行中斷服務函數time_irq,time_irq跳轉到sched_yield時間片輪轉算法從而切換進程

實驗難點

本次實驗感覺最難的兩個函數是mapper和sched_yield,分別實現加載二進制文件鏡像和進程切換的調度算法。

可以看到這兩個函數中都殘留了需要混亂的注釋,這都是debug時留下的痕跡。

mapper的難點主要在於有很多種情況需要考慮到,但是又最好能夠用一種統一的寫法將其表示出來。

sched_yiled則主要是理解調度算法的機制是什么。

不過比較搞笑的是,我現在也不能確定自己最早寫的這兩個函數是不是錯誤的。因為debug的過程中對於bug出現的准確位置一直沒能實現精准定位,所以走投無路的時候想到的往往就是這兩個最難的函數是不是錯了。結果就是這兩個函數被我重寫了很多個版本,但是最后發現的bug卻是在env_run中,而不是這兩個里面。真是讓人哭笑不得。

體會與感想

本次實驗所花費的時間再創新高,在延長了一周時間的情況下,網站統計的在線時間達到了驚人的60小時,debug的過程充滿了折磨。

究其原因還是沒辦法准確定位bug。即使采用了printf和breakpoint相結合的方式,運行到某一條匯編指令出錯的時候仍然不知道究竟在執行哪個函數。

可能是我錯誤理解了助教的意思,“`除調度算法之外其他地方沒有問題,則結果如下:”。兩種測試方法我都得到了正確的結果,我以為錯誤一定就在調度算法中,所以調度算法重寫了很多遍,也和同學討論了很久,始終不知道自己的問題出在哪。

此時根據報錯信息和prinf的定位,我又發現是調度算法執行到env_run出現的問題。

這兩者的矛盾成為了debug時間超長的罪魁禍首,因為不確定哪里是對的哪里是不對的,只能憑感覺瞎改一通,效果往往不好,甚至把正確的改成了錯誤的。

結果最后發現果然是env_run寫錯了。事實證明自己的bug還是要根據具體情況自己判斷,不能僵死地照搬助教的建議,畢竟助教給的建議是針對大部分人的,而不是針對具體一個人的bug來說。


免責聲明!

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



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