《ucore lab8》實驗報告


資源

  1. ucore在線實驗指導書
  2. 我的ucore實驗代碼

練習1: 完成讀文件操作的實現(需要編碼)

題目

首先了解打開文件的處理流程,然后參考本實驗后續的文件讀寫操作的過程分析,編寫在sfs_inode.c中sfs_io_nolock讀文件中數據的實現代碼。

請在實驗報告中給出設計實現“UNIX的PIPE機制”的概要設計方案,鼓勵給出詳細設計方案。

解答

了解打開文件的處理流程

已對打開文件的代碼流程進行了詳細分析,見lab8 源碼分析:用戶執行open的詳細流程

實現sfs_io_nolock讀文件數據的功能

根據提示不難完成編碼。原代碼中提供兩個接口sfs_rbuf和sfs_rblock,分別用於以字節和文件塊(實際上就是頁面大小)為單位來讀取文件。主要需要考慮到讀文件時的起始和結束位置可能沒與block起始位置對齊,對於不足一個block的部分調用sfs_rbuf來讀取內容,對於中間多個block的部分則調用sfs_rblock來讀取。

設計實現“UNIX的PIPE機制”(待完成)

練習2: 完成基於文件系統的執行程序機制的實現(需要編碼)

題目

改寫proc.c中的load_icode函數和其他相關函數,實現基於文件系統的執行程序機制。執行:make qemu。如果能看看到sh用戶程序的執行界面,則基本成功了。如果在sh用戶界面上可以執行“ls”、“hello”等其他放置在sfs文件系統中的其他執行程序,則可以認為本實驗基本成功。

請在實驗報告中給出設計實現基於“UNIX的硬鏈接和軟鏈接機制”的概要設計方案,鼓勵給出詳細設計方案。

解答

實現基於文件系統的執行程序機制

  1. 首先要獲取ELF文件的大小len,方法是:使用fd索引fd_array,得到對應的file,根據file->inode->sfs_inode->sfs_disk_inode->size得到ELF文件大小。

  2. 然后申請大小為len的緩沖區buf,接下來調用load_icode_read來讀取ELF文件的內容並復制到buf,接下來解析ELF文件內容並復制相應section,這部分操作與之前的實驗相同。

  3. 接下來還需要拷貝輸入參數argc和kargv到用戶棧頂,並根據輸入參數所占的內存大小修改tf_esp的值。這樣當程序沿着load_icode -> do_execve -> sys_exec -> syscall -> trap_dispatch -> trap -> trapret一路返回並執行iret時,就能從棧頂上面的幾個位置獲取到argc和argv的值。

設計實現基於“UNIX的硬鏈接和軟鏈接機制”(待完成)

Bug 1:運行用戶程序sh時內存訪問異常

問題描述

編碼完成后,執行sudo make qemu,查看輸出日志,發現沒有進入sh界面,提示錯誤信息:“not valid addr b0000000, and can not find it in vma”。

定位流程
  1. 首先確認0xb0000000這個地址是何時被訪問的。印象中這是用戶棧頂的地址,查看load_icode的實現,果然發現在結尾處把tf_esp設置為USTACKTOP 0xb0000000,后面沿着do_execve -> sys_exec -> syscall -> trap_dispatch -> trap -> trapret一路返回,在trapret的結尾處執行iret,由於發生特權級轉換,這時會把esp寄存器設置為0xb0000000.使用gdb調試發現,在進入用戶程序的開頭,會訪問esp所指的內存,這時就發生缺頁異常。

  2. 分析可能的原因:

    • USTACKTOP這個地址本來就不能訪問?
    • 內核態到用戶態的切換不成功?
    • 頁目錄表和頁表設置有誤?
    • 用戶程序sh有問題?
    • 用戶程序的輸入參數沒設置?
  3. 使用gdb調試,在執行iret的地方使用si逐步調試,發現執行iret后,首先跳到地址為0x008004e9的位置運行,打開sh.asm查看對應位置的匯編代碼,發現0x008004e9是start的起始位置,start的第二條指令movl (%esp), ebx會訪問到0xb0000000,目的是加載argc參數。那么原因大概確定了,應該是我在load_icode函數中沒設置好輸入參數,導致現在訪問輸入參數失敗。

_start:
    # set ebp for backtrace
    movl $0x0, %ebp
  8004e9:	bd 00 00 00 00       	mov    $0x0,%ebp

    # load argc and argv
    movl (%esp), %ebx
  8004ee:	8b 1c 24             	mov    (%esp),%ebx
    lea 0x4(%esp), %ecx
  8004f1:	8d 4c 24 04          	lea    0x4(%esp),%ecx
    
    # move down the esp register since it may cause page fault in backtrace
    subl $0x20, %esp
  8004f5:	83 ec 20             	sub    $0x20,%esp

    # save argc and argv on stack
    pushl %ecx
  8004f8:	51                   	push   %ecx
    pushl %ebx
  8004f9:	53                   	push   %ebx

    # call user-program function
    call umain
  8004fa:	e8 9d 04 00 00       	call   80099c <umain>
  1. 那么為什么lab7不會有問題呢?於是回頭看lab7的代碼,首先查看lab7用戶程序的start的匯編代碼,發現果然有區別:lab8需要加載argc和argv,lab7則不需要加載。為什么會有這個差別?
_start:
    # set ebp for backtrace
    movl $0x0, %ebp
  8003fb:	bd 00 00 00 00       	mov    $0x0,%ebp

    # move down the esp register
    # since it may cause page fault in backtrace
    subl $0x20, %esp
  800400:	83 ec 20             	sub    $0x20,%esp

    # call user-program function
    call umain
  800403:	e8 f9 00 00 00       	call   800501 <umain>
  1. 查看兩個lab的umain的實現,終於找到原因:lab8的umain函數定義含有argc和argv兩個輸入參數,lab7的umain函數定義無輸入參數。
// lab8
void umain(int argc, char *argv[]) {
    int fd;
    if ((fd = initfd(0, "stdin:", O_RDONLY)) < 0) {
        warn("open <stdin> failed: %e.\n", fd);
    }
    if ((fd = initfd(1, "stdout:", O_WRONLY)) < 0) {
        warn("open <stdout> failed: %e.\n", fd);
    }
    int ret = main(argc, argv);
    exit(ret);
}

// lab7
void umain(void) {
    int ret = main();
    exit(ret);
}
  1. 但還是有疑問:即使我沒在棧頂設置好argc和argv,也應該能訪問棧頂吧?只是說訪問到的數據是不確定的而已啊。會不會是0xb0000000剛好是禁止訪問的邊界地址?我試着將0xb0000000 - 16賦值給tf_esp,再運行,竟然不報錯了!這時可以進入sh界面,輸入ls沒反應,輸入hello或forktest等命令則正常運行。總之,這基本證明了我的猜想是正確的。

  2. 接下來不難得到正確解法:0xb0000000是用戶地址邊界,如果要存儲argc和argv,則需要減去相應的值,在0xb0000000的前面來存儲。修改后再運行,果然能正常進入sh界面,而且執行ls也能正常輸出了。

  3. 最后的疑問:步驟6只是避免了訪問地址邊界,但沒有正確設置argc和argv(使用gdb調試時發現此時argc和argv都設置為0了),為什么大部分命令也能正常運行,唯獨ls運行異常(無輸出)?查看ls.c文件,發現當輸入argc為0時,確實不會執行任何操作;argc為1時,則會執行ls ..


免責聲明!

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



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