操作系統ucore lab3實驗報告

操作系統lab3實驗報告

本次實驗主要完成ucore內核對虛擬內存的管理工作。其總體設計思路還是比較簡單,即首先完成初始化虛擬內存管理機制,即需要設置好哪些頁需要放在物理內存中,哪些頁不需要放在物理內存中,而是可被換出到硬盤上,並涉及完善建立頁表映射、頁錯誤異常處理操作等函數實現。然後就執行一組訪存測試,看看我們建立的頁表項是否能夠正確完成虛實地址映射,是否正確描述了虛擬內存頁在物理內存中還是在硬盤上,是否能夠正確把虛擬內存頁在物理內存和硬盤之間進行傳遞,是否正確實現了頁面替換算法等。

練習0 填寫已有實驗

同樣與lab2類似,我使用了名爲meld的一款文件比對工具,直接比對lab2lab3兩個文件夾,它就能把其中不相同的部分列舉出來,然後比對進行修改即可。大致截圖如下:

比對修改之後,大致羅列一下有以下文件需要我們進行修改:

default_pmm.c
pmm.c
trap.c

練習0主要要讓我們修改補充的就是上述四個文件。直接在meld裏面對比複製就行了。也沒有什麼技術含量。

練習1 給未被映射的地址映射上物理頁

本實驗要求完成 do_pgfault函數,作用給未被映射的地址映射上物理頁。
具體而言,當啓動分頁機制以後,如果一條指令或數據的虛擬地址所對應的物理頁框不在內
存中或者訪問的類型有錯誤(比如寫一個只讀頁或用戶態程序訪問內核態的數據等),就會發生
頁錯誤異常。產生頁面異常的原因主要有:

  • 目標頁面不存在(頁表項全爲0,即該線性地址與物理地址尚未建立映射或者已經撤銷);
  • 相應的物理頁面不在內存中(頁表項非空,但Present標誌位=0,比如在swap分區或磁盤文件上)
  • 訪問權限不符合(此時頁表項P標誌=1,比如企圖寫只讀頁面).

當出現上面情況之一,那麼就會產生頁面page fault(#PF)異常。產生異常的線性地址存儲在
CR2中,並且將是page fault的產生類型保存在 error code 中

那麼我們的這個do_pgfault函數的思路就明顯了。do_pgfault()函數從CR2寄存器中獲取頁錯誤異常的虛擬地址,根據error code來查找這個虛擬地址是否在某一個VMA的地址範圍內,那麼就給它分配一個物理頁。
這裏的VMA是描述應用程序對虛擬內存“需求”的變量,如下:

    struct vma_struct {  
        // the set of vma using the same PDT  
        struct mm_struct *vm_mm;  
        uintptr_t vm_start;      // start addr of vma  
        uintptr_t vm_end;      // end addr of vma  
        uint32_t vm_flags;     // flags of vma  
        //linear list link which sorted by start addr of vma  
        list_entry_t list_link;  
    };  
  • vm_startvm_end描述的是一個合理的地址空間範圍(即嚴格確保 vm_start < vm_end的關係);
  • list_link是一個雙向鏈表,按照從小到大的順序把一系列用vma_struct表示的虛擬內存空間鏈接起來,並且還要求這些鏈起來的vma_struct應該是不相交的,即vma之間的地址空間無交集;
  • vm_flags表示了這個虛擬內存空間的屬性,目前的屬性包括

    > #define VM_READ 0x00000001   //只讀
      #define VM_WRITE 0x00000002  //可讀寫
      #define VM_EXEC 0x00000004   //可執行
    
  • vm_mm是一個指針,指向一個比vma_struct更高的抽象層次的數據結構mm_struct
    而這mm_struct包含所有虛擬內存空間的共同屬性,如下:

    struct mm_struct {  
        // linear list link which sorted by start addr of vma  
        list_entry_t mmap_list;  
        // current accessed vma, used for speed purpose  
        struct vma_struct *mmap_cache;  
        pde_t *pgdir; // the PDT of these vma  
        int map_count; // the count of these vma  
        void *sm_priv; // the private data for swap manager  
    };  
  • mmap_list是雙向鏈表頭,鏈接了所有屬於同一頁目錄表的虛擬內存空間
  • mmap_cache是指向當前正在使用的虛擬內存空間
  • pgdir所指向的就是 mm_struct數據結構所維護的頁表
  • map_count記錄mmap_list裏面鏈接的vma_struct的個數
  • sm_priv指向用來鏈接記錄頁訪問情況的鏈表頭

最後具體的實現如下:(這裏只摘錄了我填寫的部分)

    if ((ptep = get_pte(mm->pgdir, addr, 1)) == NULL) { //目標頁面不存在,失敗
        cprintf("get_pte in do_pgfault failed\n");
        goto failed;
    }

    if (*ptep == 0) {   //權限不夠,也是失敗!
        if (pgdir_alloc_page(mm->pgdir, addr, perm) == NULL) {
            cprintf("pgdir_alloc_page in do_pgfault failed\n");
            goto failed;
        }
    }
    else {   //頁表項非空,可以嘗試換入頁面 
        if(swap_init_ok) {
            struct Page *page=NULL;//根據mm結構和addr地址,嘗試將硬盤中的內容換入至page中
            if ((ret = swap_in(mm, addr, &page)) != 0) {
                cprintf("swap_in in do_pgfault failed\n");
                goto failed;
            }    
            page_insert(mm->pgdir, page, addr, perm);//建立虛擬地址和物理地址之間的對應關係 
            swap_map_swappable(mm, addr, page, 1);//將此頁面設置爲可交換的  
            page->pra_vaddr = addr;
        }
        else {
            cprintf("no swap_init_ok but ptep is %x, failed\n",*ptep);
            goto failed;
        }
   }

練習2 補充完成基於FIFO()的頁面替換算法

頁錯誤異常發生時,有可能是因爲頁面保存在swap區或者磁盤文件上造成的,所以我們需要通過頁面分配解決這個問題。
頁面替換主要分爲兩個方面,頁面換出和頁面換入。

  • 頁面換入主要在上述的do_pgfault()函數實現;
  • 頁面換出主要在swap_out_vistim()函數實現。

這裏換入在練習1已經完成了,這裏就主要介紹換出。
FIFO替換算法會維護一個隊列,隊列按照頁面調用的次序排列,越早被加載到內存的頁面會越早被換出。
具體實現的函數如下:
首先是_fifo_map_swappable(),它的主要作用是將最近被用到的頁面添加到算法所維護的次序隊列。

static int _fifo_map_swappable(struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in) {        
        list_entry_t *head=(list_entry_t*) mm->sm_priv;   
        list_entry_t *entry=&(page->pra_page_link);          
        assert(entry != NULL && head != NULL);   
        list_add(head, entry); //將最近用到的頁面添加到次序隊尾  
        return 0;   
    } 

然後是_fifo_swap_out_victim()函數是用來查詢哪個頁面需要被換出,它的主要作用是用來查詢哪個頁面需要被換出。

static int  
_fifo_swap_out_victim(struct mm_struct *mm, struct Page ** ptr_page, int in_tick) {       
    list_entry_t *head=(list_entry_t*) mm->sm_priv;          
    assert(head != NULL);      
    assert(in_tick==0);
    list_entry_t *le = head->prev;  //用le指示需要被換出的頁             
    assert(head!=le);  
    struct Page *p = le2page(le, pra_page_link);//le2page宏可以根據鏈表元素獲得對應的Page指針p      
    list_del(le);      //將進來最早的頁面從隊列中刪除      
    assert(p !=NULL);       
    *ptr_page = p; //將這一頁的地址存儲在ptr_page中
    return 0; 
}

結果截圖

由於在虛擬機內部,而且滾屏稍快,所以這裏截圖如下:


這裏可以看到如下部分:

........
check_vma_struct() succeeded!
page fault at 0x00000100: K/W [no page found].
check_pgfault() succeeded!
check_vmm() succeeded.
........

然後是下圖:

可以看到這個

check_swap() succeeded!

所以實驗成功

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