1. 程式人生 > 其它 >BUAA_OS lab4實驗報告

BUAA_OS lab4實驗報告

一、思考題

1. 思考4.1

思考並回答下面的問題:

• 核心在儲存現場的時候是如何避免破壞通用暫存器的?

• 系統陷入核心呼叫後可以直接從當時的$a0-$a3 引數暫存器中得到使用者呼叫msyscall 留下的資訊嗎?

• 我們是怎麼做到讓sys 開頭的函式“認為”我們提供了和使用者呼叫msyscall 時同樣的引數的?

• 核心處理系統呼叫的過程對Trapframe 做了哪些更改?這種修改對應的使用者態的變化是?

  • 呼叫SAVE_ALL巨集函式,以sp為基地址將所有暫存器值存入。

  • 能,因為呼叫函式時預設前四個引數傳入$a0-$a3暫存器。但在核心態中可能使用這些暫存器進行一些操作計算,此時暫存器原有值被改變,因此再次以這些引數呼叫其他函式時需要重新以sp為基地址,按相應偏移從使用者棧中取用這四個暫存器值。

  • 將呼叫函式時都將前四個引數按順序放入$a0-$a3暫存器,後兩個引數按順序存入核心棧中的相同位置(相對sp偏移相同)。

  • 涉及到Trapframe修改的部分程式碼:

    lw t0, TF_EPC(sp)
    addiu t0, t0, 4
    sw t0, TF_EPC(sp)

    將EPC加4,使得系統呼叫後pc指向呼叫時的下一條指令。

2. 思考4.2

思考下面的問題,並對這兩個問題談談你的理解:

• 子程序完全按照fork() 之後父程序的程式碼執行,說明了什麼?

• 但是子程序卻沒有執行fork() 之前父程序的程式碼,又說明了什麼?

  • 子程序的程式碼段與父程序相同(子程序程式碼段共享了父程序的物理空間)。

  • 子程序恢復的上下文位置為fork函式接下來的位置。

3. 思考4.3

關於fork 函式的兩個返回值,下面說法正確的是:

A. fork 在父程序中被呼叫兩次,產生兩個返回值

B. fork 在兩個程序中分別被呼叫一次,產生兩個不同的返回值

C. fork 只在父程序中被呼叫了一次,在兩個程序中各產生一個返回值

D. fork 只在子程序中被呼叫了一次,在兩個程序中各產生一個返回值

答:C

4. 思考4.4

如果仔細閱讀上述這一段話, 你應該可以發現, 我們並不是對所有的使用者空間頁都使用duppage 進行了保護。那麼究竟哪些使用者空間頁可以保護,哪些不可以呢,請結合include/mmu.h 裡的記憶體佈局圖談談你的看法。

在0 ~ USTACKTOP範圍的記憶體,除只讀、共享的頁面外都需要設定PTE_COW進行保護。

5. 思考4.5

在遍歷地址空間存取頁表項時你需要使用到vpd 和vpt 這兩個“指標的指標”,請思考並回答這幾個問題:

• vpt 和vpd 的作用是什麼?怎樣使用它們?

• 從實現的角度談一下為什麼能夠通過這種方式來存取程序自身頁表?

• 它們是如何體現自對映設計的?

• 程序能夠通過這種存取的方式來修改自己的頁表項嗎?

  • vpd是指向使用者頁目錄的指標,以*vpd(指向陣列中第一個元素的指標)為基地址,加上頁目錄項偏移數即可指向va對應頁目錄項,即((Pde*)(*vpd)) + (va >> 22);vpt是指向使用者頁表的指標陣列的指標,以*vpt(指向陣列中第一個元素的指標)為基地址,加上頁表項偏移數即可指向va對應的頁表項,即((Pte*)(*vpt)) + (va >> 12)

  • 在user/entry.S中定義了vpt和vpd,它們分別指向UVPT和(UVPT + (UVPT >> 12) * 4),即使用者頁表和使用者頁目錄的虛擬地址,有了基地址,和從虛擬地址中獲得的偏移,即可實現對自身程序頁表的操作。

  • 注意到,vpd的地址在UVPT和UVPT + PDMAP之間,說明將頁目錄對映到了某一頁表位置,即實現了自對映。

  • 不能。因為使用者態不能修改頁表項。

6. 思考4.6

page_fault_handler 函式中,你可能注意到了一個向異常處理棧複製Trapframe 執行現場的過程,請思考並回答這幾個問題:

• 這裡實現了一個支援類似於“中斷重入”的機制,而在什麼時候會出現這種“中斷重入”?

• 核心為什麼需要將異常的現場Trapframe 複製到使用者空間?

  • 中斷重入可能是因為在處理缺頁中斷時又發生了中斷。

  • 在我們的作業系統中,異常是在使用者態處理的,需要根據異常現場的Trapframe進行處理;同時,需要儲存現場的暫存器值,防止其被破壞。

7. 思考4.7

到這裡我們大概知道了這是一個由使用者程式處理並由使用者程式自身來恢復執行現場的過程,請思考並回答以下幾個問題:

• 使用者處理相比於在核心處理寫時複製的缺頁中斷有什麼優勢?

• 從通用暫存器的用途角度討論使用者空間下進行現場的恢復是如何做到不破壞通用暫存器的?

  • 儘量減少核心出現錯誤的可能,即使程式崩潰,也不會影響系統的穩定,同時微核心的模式下,使用者態進行新頁面的分配對映也更加靈活方便。

  • 通用暫存器只是用於CPU計算處理,在巨集觀來看,得益於模組化的處理結構,且這一步並不是系統突發的異常,而是順次執行到的,因此進入現場恢復階段前的計算結果都已完成利用或存入了相應區域,此時通用暫存器中沒有需要保護的內容了,因此可以隨意覆蓋。

8. 思考4.8

請思考並回答以下幾個問題:

• 為什麼需要將set_pgfault_handler 的呼叫放置在syscall_env_alloc 之前?

• 如果放置在寫時複製保護機制完成之後會有怎樣的效果?

• 子程序需不需要對在entry.S 定義的字__pgfault_handler 賦值?

  • 用於處理在alloc過程中發生的缺頁中斷。

  • 由於無法處理缺頁中斷錯誤,寫時複製保護機制不能執行。

  • 不需要。該值已在父程序中設定,子程序只需與父程序保持一致。

二、實驗難點

1. handle_sys函式

根據我的理解,使用者態和核心態的棧空間大致按如圖所示的關係操作,在跳轉到相應的系統呼叫函式之前,前四個引數可以由暫存器傳遞,但後兩個引數需要通過開闢新的棧空間傳遞。

2. 程序通訊

  • sys_ipc_recv

    • 設定允許接收

    • 設定接受資訊的地址dstva

    • 程序進入阻塞狀態ENV_NOT_RUNNABLE

  • sys_ipc_can_send

    • 通過envid找到接收訊息的程序

    • value表示傳遞的資訊

    • 設定訊息來源(程序id)

    • 若涉及傳遞物理頁面,srcva != 0,此時需要向被通訊程序的dstva對映來自srcva的頁面

    • 被通訊程序就緒ENV_RUNNABLE

  • ipc_send

    • 不斷向訊息接收的程序傳送訊息,直到該程序成功接收

  • ipc_recv

    • 設定接收訊息程序接受資訊的地址dstva

    • 通過指標傳遞訊息來源(程序id)

3. fork函式

對程序使用者空間頁設定保護時,需要先檢查頁目錄項是否有效,再檢查頁表項是否有效,否則若先檢查頁表項,將出現大量pageout。

4. duppage函式

需要寫時複製保護的頁面:可寫&&非共享&&非寫時複製,此時需要對父子分別設定PTE_COW位保護(即分別進行map)。

注意進行空間對映時需要先對映子程序,再對映父程序。原因:整個duppage的時間較長,本地執行大概需要幾秒的時間,這期間將進行無數次時鐘中斷,若恰好在map父程序結束而尚未map子程序時發生中斷,而這一頁又恰好是duppage函式執行所在頁,此時回到duppage時繼續迴圈,會對迴圈變數i進行寫操作,於是觸發寫時複製,為子程序映射了一個新的頁,這樣結果將與評測機不符。

三、體會與感想

本次lab用時約30h(好久),期間經歷了無數的bug,可以說甚至遍歷了幾乎所有分數組合,當然還有很多地方沒有搞懂,估計也搞不懂了,邏輯上似乎沒有什麼毛病,但執行結果就是奇奇怪怪,而且過不了評測。試圖詢問助教,但似乎助教de了一天也不知道為什麼會有這樣的結果,遂改為了大眾寫法。lab4-2的Extra更是過得非常魔幻,課上邏輯沒有什麼問題,但就是隻能過1個點,甚至本地寫了好幾個測試程式都能正確執行,後來破釜沉舟改了一下sys_set_env_status之後居然過了,但我非常肯定地覺得這個改後的邏輯不對,所以我非常疑惑,於是又找助教de了一天,依舊無果,攤手。

總之體驗並不是很好,感覺bug像個小惡魔趴在暗處盯著我,但我找不出它來,本不想不了了之,但奈何...一群人都不知道為什麼,時間有限,只能匆匆開啟下一個lab了。

四、殘留難點

1. lab4-1

起初我的邏輯與多數人似乎並不相同,我在sys_ipc_recv函式中程序將自己狀態設為ENV_NOT_RUNNABLE時將程序從排程佇列中移除了,在sys_ipc_can_send函式中將程序狀態設為ENV_RUNNABLE時又插入了排程佇列,但這樣做評測時一直是4/7,不知道為什麼。後來只能改成通訊時不操作排程佇列,讓阻塞的程序留在佇列裡。疑惑。

2. lab4-2

起初我的邏輯是在sys_env_alloc的時候將程序加入排程佇列(此時程序為ENV_NOT_RUNNABLE,理論上不會有副作用),而在sys_set_env_status時由於程序已經處於排程佇列中,可直接改變程序狀態。上機時因為評測始終無法通過,我將邏輯改為僅在sys_set_env_status時將程序插入或移出排程佇列,竟然通過了!(事實上,我認為這種做法是不合理的,因為在lab4-1中程序通訊時阻塞的佇列沒有被移出,此時若僅在阻塞變為就緒時將程序插入佇列,可能導致同一程序在佇列中出現兩次?)疑惑。