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

BUAA_OS lab6實驗報告

一、思考題

1. 思考6.1

示例程式碼中,父程序操作管道的寫端,子程序操作管道的讀端。如果現在想讓父程序作為“讀者”,程式碼應當如何修改?

修改後程式碼如下(交換case 0 和default部分的程式碼):

#include <stdlib.h>
#include <unistd.h>
int fildes[2];
/* buf size is 100 */
char buf[100];
int status;
int main(){
status = pipe(fildes);
if (status == -1 ) {
/* an error occurred */
printf("error\n");
}
switch (fork()) {
case -1: /* Handle error */
break;
case 0: /* Child - writes to pipe */
close(fildes[0]); /* Read end is unused */
write(fildes[1], "Hello world\n", 12); /* Write data on pipe */
close(fildes[1]); /* Child will see EOF */
exit(EXIT_SUCCESS);
default: /* Parent - reads from pipe */
close(fildes[1]); /* Write end is unused */
read(fildes[0], buf, 100); /* Get data from pipe */
printf("parent-process read:%s",buf); /* Print the data */
close(fildes[0]); /* Finished with pipe */
exit(EXIT_SUCCESS);
}
}

2. 思考6.2

上面這種不同步修改 pp_ref 而導致的程序競爭問題在 user/fd.c 中的dup 函式中也存在。請結合程式碼模仿上述情景,分析一下我們的 dup 函式中為什麼會出現預想之外的情況?

考慮這樣的虛擬碼:

if(fork() == 0) {
dup(p[1]);
read(p[0]);
}
else {
dup(p[0]);
write(p[1]);
}

注意到在dup函式中,先對fd執行syscall_mem_map,後對data執行syscall_mem_map。

子程序先開始執行,執行到dup和read之間時發生時鐘中斷,此時pageref(p[1]) == 1,pageref(pipe) == 1;父程序開始執行,在dup函式中執行到fd的map之後,data的map之前,此時pageref(p[0]) == 1,pageref(pipe) == 1;時鐘中斷,子程序再次開始執行,此時執行read函式,判斷髮現pageref(p[0]) == pageref(pipe),誤認為是寫者全部結束,於是錯誤地返回了。

3. 思考6.3

閱讀上述材料並思考:為什麼系統呼叫一定是原子操作呢?如果你覺得不是所有的系統呼叫都是原子操作,請給出反例。希望能結合相關程式碼進行分析。

系統呼叫一定是原子操作,因為在通過系統呼叫陷入核心態時彙編程式碼關閉了時鐘中斷。

4. 思考6.4

仔細閱讀上面這段話,並思考下列問題

• 按照上述說法控制 pipeclose 中 fd 和 pipe unmap 的順序,是否可以解決上述場景的程序競爭問題?給出你的分析過程。

• 我們只分析了 close 時的情形,在 fd.c 中有一個 dup 函式,用於複製檔案內容。試想,如果要複製的是一個管道,那麼是否會出現與 close 類似的問題?請模仿上述材料寫寫你的理解。

  • 可以解決。最初pageref(p[0]) = 2,pageref(p[1]) = 2,pageref(pipe) = 4;仍考慮子程序先執行,對p[1]執行close函式時先解除了對寫端fd的對映,之後發生時鐘中斷,此時pageref(p[0]) = 2,pageref(p[1]) = 1,pageref(pipe) = 4;父程序執行完close(p[0])後,pageref(p[0]) = 1,pageref(p[1]) = 1,pageref(pipe) = 3,父程序開始執行時仍不滿足寫端關閉的條件,因此競爭問題沒有出現。

  • 在程式執行時,總滿足pageref(p[0]) ≤ pageref(pipe),如果在dep中先對fd進行對映,此時fd的rep先增加,可能填補了二者的空隙,造成讀端已滿的假象(寫端同理),因此也會出現與close相似的問題,需要調整對映的順序。

5. 思考6.5

bss 在 ELF 中並不佔空間,但 ELF 載入進記憶體後,bss 段的資料佔據了空間,並且初始值都是 0。請回答你設計的函式是如何實現上面這點的?

與lab3實現幾乎完全相同,當系統呼叫執行sys_mem_alloc函式時進一步呼叫了page_alloc函式,其中有bzero使新分配的頁面內容全部為0,只要不向資料段錯誤地複製內容,即可保證該段初始值為0。

6. 思考6.6

為什麼我們的 *.b 的 text 段偏移值都是一樣的,為固定值?

觀察user/user.lds檔案內容,有如下程式碼:

. = 0x00400000;

_text = .; /* Text and read-only data */
.text : {
*(.text)
*(.fixup)
*(.gnu.warning)
}

這說明所有檔案在連結時都以此規定相同的起始地址。

7.思考6.7

在哪步,0和1被“安排”為標準輸入和標準輸出?請分析程式碼執行流程,給出答案。

在user/init.c中,有如下程式碼,在此處將0和1安排為了標準輸入輸出:

close(0);
if ((r = opencons()) < 0)
user_panic("opencons: %e", r);
if (r != 0)
user_panic("first opencons used fd %d", r);
if ((r = dup(0, 1)) < 0)
user_panic("dup: %d", r);

二、實驗難點

1. 管道與程序切換

由於時鐘中斷可能在函式執行中間發生,而有時需要保證同步性,因此需要注意判斷是否在同一時間片內。_pipeisclosed函式中就需要運用env_runs變數計數程序執行次數,只有在同一次數內才能保證pageref的同步性。

2. spawn

在載入ELF檔案內容時,需要呼叫自己寫的usr_load_elf函式,這與lab3中的load_icode_mapper相似,不同之處在於本次位於使用者態下,而要實現兩個程序間內容的傳遞,需要通過系統呼叫,將檔案系統程序的一個暫時頁面對映到子程序所需頁面,之後檔案系統程序在該頁面的具體位置複製內容,子程序相應位置也就有了資訊,最後檔案系統程序再解除工具頁面的對映,開始下一個位置的對映。

三、體會與感想

本次lab用時13小時,相比於前幾個lab用時少了很多,也可能是因為到了考期,而且沒有了課上測試的要求,有些程式碼沒有很認真地理解,做得有些潦草。計劃考期結束後重新過一遍,加深對管道的理解。

實現完檔案系統和管道後,作業系統似乎更加有意思了起來,原來只是對著一塊黑黑的螢幕寫一些十分抽象的程式碼,執行結果也只是一些printf的輸出,而現在可以自己建立檔案,可以實現控制檯中斷,與控制檯互動等,隨便敲敲都會有顯示,於是像個沒見過世面的小孩子,覺得驚喜又有趣。

四、殘留難點

對填寫範圍以外的官方程式碼理解還不透徹,需要鞥更細緻的學習。