1. 程式人生 > >作業系統實驗三實驗報告

作業系統實驗三實驗報告

實驗三:虛擬記憶體管理

練習0:填寫已有實驗

參考實驗二中的做法,使用meld可以很快完成

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

1.1 ucore虛擬記憶體機制

ucore的虛擬記憶體管理總體框架主要包含

  • 完成初始化虛擬記憶體管理機制:IDE硬碟讀寫、缺頁異常處理

  • 設定虛擬頁空間和物理頁幀空間,表述不在實體記憶體中的“合法”虛擬頁

  • 完善建立頁表對映、頁訪問異常處理操作等函式實現

基本構成下圖中的結構

這裡寫圖片描述

關鍵資料結構mm_structvma_struct定義在kern/mm/vmm.h中,vma_struct描述了應用程式對虛擬記憶體的需求,mm_struct

是一個比前者更高抽象層次的資料結構,表示包含了所有虛擬記憶體空間的共同屬性

// the control struct for a set of vma using the same PDT
struct mm_struct {
    list_entry_t mmap_list;        // linear list link which sorted by start addr of vma
    struct vma_struct *mmap_cache; // current accessed vma, used for speed purpose
    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 }; // the virtual continuous memory area(vma), [vm_start, vm_end), // addr belong to a vma means vma.vm_start<= addr <vma.vm_end struct vma_struct { struct
mm_struct *vm_mm; // the set of vma using the same PDT uintptr_t vm_start; // start addr of vma uintptr_t vm_end; // end addr of vma, not include the vm_end itself uint32_t vm_flags; // flags of vma list_entry_t list_link; // linear list link which sorted by start addr of vma };
  • mmap_list連結屬於同一個頁目錄表的所有vma_struct
  • mmap_cache指向當前正在使用的虛擬記憶體空間,基於區域性性原理對連結串列查詢加速
  • pgdir指向頁目錄表
  • map_count計數vma_struct的個數
  • sm_priv指向用來記錄訪問情況的連結串列
  • vm_mm指向所屬的mm_struct
  • vm_start記錄這個區域地址的起始
  • vm_end記錄這個區域地址的結束
  • vm_flags標記區域屬性,目前在vmm.h中巨集定義有VM_READVM_WRITEVM_EXEC三種標記
  • list_link根據地址從低到高連結了所有vma_struct,連結的不同區域在虛擬記憶體空間是不相交的

1.2 Page Fault異常處理

在程式的執行過程中由於某種原因而使 CPU 無法最終訪問到相應的實體記憶體單元,即無法完成從虛擬地址到實體地址對映時,CPU 會產生一次頁訪問異常,從而需要進行相應的頁訪問異常的中斷服務例程。當相關處理完成後,頁訪問異常服務例程會返回到產生異常的指令處重新執行,使得應用軟體可以繼續正常執行下去。

具體而言,當啟動分頁機制以後,如果一條指令或資料的虛擬地址所對應的物理頁框不在記憶體中或者訪問的型別有錯誤(比如寫一個只讀頁或使用者態程式訪問核心態的資料等),就會發生頁訪問異常。產生頁訪問異常的原因主要有:

  • 目標頁幀不存在(表現為頁表項全為0,程式碼中的*pdep==0
  • 相應的物理頁幀不在記憶體中(表現為頁表項非空,但是Present==0,需要進一步執行換頁處理(練習3))
  • 不滿足訪問要求(表現為Present==1,但是低許可權程式訪問高許可權地址空間或是嘗試寫只允許讀的頁)

當出現缺頁異常時,CPU會把產生異常的線性地址儲存在CR2中,並且把表示一場型別的值errorCode儲存在中斷棧中,系統處理基本呼叫流程為trap->trap_dispatch->pgfault_handler->do_pgfault,本實驗的重點之一在於實現do_pgfault(),程式碼如下

/* do_pgfault - interrupt handler to process the page fault execption
 * @mm         : the control struct for a set of vma using the same PDT
 * @error_code : the error code recorded in trapframe->tf_err which is setted by x86 hardware
 * @addr       : the addr which causes a memory access exception, (the contents of the CR2 register)
 *
 * CALL GRAPH: trap--> trap_dispatch-->pgfault_handler-->do_pgfault
 * The processor provides ucore's do_pgfault function with two items of information to aid in diagnosing
 * the exception and recovering from it.
 *   (1) The contents of the CR2 register. The processor loads the CR2 register with the
 *       32-bit linear address that generated the exception. The do_pgfault fun can
 *       use this address to locate the corresponding page directory and page-table
 *       entries.
 *   (2) An error code on the kernel stack. The error code for a page fault has a format different from
 *       that for other exceptions. The error code tells the exception handler three things:
 *         -- The P flag   (bit 0) indicates whether the exception was due to a not-present page (0)
 *            or to either an access rights violation or the use of a reserved bit (1).
 *         -- The W/R flag (bit 1) indicates whether the memory access that caused the exception
 *            was a read (0) or write (1).
 *         -- The U/S flag (bit 2) indicates whether the processor was executing at user mode (1)
 *            or supervisor mode (0) at the time of the exception.
 */
int
do_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr) {
    int ret = -E_INVAL;
    //try to find a vma which include addr
    struct vma_struct *vma = find_vma(mm, addr);

    pgfault_num++;
    //If the addr is in the range of a mm's vma?
    if (vma == NULL || vma->vm_start > addr) {
        cprintf("not valid addr %x, and  can not find it in vma\n", addr);
        goto failed;
    }
    //check the error_code
    switch (error_code & 3) {
    default:
            /* error code flag : default is 3 ( W/R=1, P=1): write, present */
    case 2: /* error code flag : (W/R=1, P=0): write, not present */
        if (!(vma->vm_flags & VM_WRITE)) {
            cprintf("do_pgfault failed: error code flag = write AND not present, but the addr's vma cannot write\n");
            goto failed;
        }
        break;
    case 1: /* error code flag : (W/R=0, P=1): read, present */
        cprintf("do_pgfault failed: error code flag = read AND present\n");
        goto failed;
    case 0: /* error code flag : (W/R=0, P=0): read, not present */
        if (!(vma->vm_flags & (VM_READ | VM_EXEC))) {
            cprintf("do_pgfault failed: error code flag = read AND not present, but the addr's vma cannot read or exec\n");
            goto failed;
        }
    }
    /* IF (write an existed addr ) OR
     *    (write an non_existed addr && addr is writable) OR
     *    (read  an non_existed addr && addr is readable)
     * THEN
     *    continue process
     */
    uint32_t perm = PTE_U;
    if (vma->vm_flags & VM_WRITE) {
        perm |= PTE_W;
    }
    addr = ROUNDDOWN(addr, PGSIZE);

    ret = -E_NO_MEM;

    pte_t *ptep=NULL;
    /*LAB3 EXERCISE 1: YOUR CODE
    * Maybe you want help comment, BELOW comments can help you finish the code
    *
    * Some Useful MACROs and DEFINEs, you can use them in below implementation.
    * MACROs or Functions:
    *   get_pte : get an pte and return the kernel virtual address of this pte for la
    *             if the PT contians this pte didn't exist, alloc a page for PT (notice the 3th parameter '1')
    *   pgdir_alloc_page : call alloc_page & page_insert functions to allocate a page size memory & setup
    *             an addr map pa<--->la with linear address la and the PDT pgdir
    * DEFINES:
    *   VM_WRITE  : If vma->vm_flags & VM_WRITE == 1/0, then the vma is writable/non writable
    *   PTE_W           0x002                   // page table/directory entry flags bit : Writeable
    *   PTE_U           0x004                   // page table/directory entry flags bit : User can access
    * VARIABLES:
    *   mm->pgdir : the PDT of these vma
    *
    */
    ptep = get_pte(mm->pgdir, addr, 1);         //try to find a pte
    if(ptep == NULL){
        cprintf("do_pgfault failed: get_pte returns NULL\n");
        goto failed;
    }
#if 0
    /*LAB3 EXERCISE 1: YOUR CODE*/
    ptep = ???              //(1) try to find a pte, if pte's PT(Page Table) isn't existed, then create a PT.
    if (*ptep == 0) {
                            //(2) if the phy addr isn't exist, then alloc a page & map the phy addr with logical addr

    }
    else {
    /*LAB3 EXERCISE 2: YOUR CODE
    * Now we think this pte is a  swap entry, we should load data from disk to a page with phy addr,
    * and map the phy addr with logical addr, trigger swap manager to record the access situation of this page.
    *
    *  Some Useful MACROs and DEFINEs, you can use them in below implementation.
    *  MACROs or Functions:
    *    swap_in(mm, addr, &page) : alloc a memory page, then according to the swap entry in PTE for addr,
    *                               find the addr of disk page, read the content of disk page into this memroy page
    *    page_insert : build the map of phy addr of an Page with the linear addr la
    *    swap_map_swappable : set the page swappable
    */
        if(swap_init_ok) {
            struct Page *page=NULL;
                                    //(1)According to the mm AND addr, try to load the content of right disk page
                                    //    into the memory which page managed.
                                    //(2) According to the mm, addr AND page, setup the map of phy addr <---> logical addr
                                    //(3) make the page swappable.
        }
        else {
            cprintf("no swap_init_ok but ptep is %x, failed\n",*ptep);
            goto failed;
        }
    }
#endif
    if(*ptep == 0){                             //if the physical addr which is pt's content(*petp) isn't exist, then call pgdir_alloc_page()
        if(pgdir_alloc_page(mm->pgdir, addr, perm) == NULL){
            cprintf("do_pgfault failed: pgdir_alloc_page returns NULL\n");
            goto failed;
        }
    }
    else{
        if(swap_init_ok){
            struct Page *page = NULL;
            if((ret = swap_in(mm, addr, &page)) != 0){
                cprintf("do_pgfault failed: swap_in returns %d",ret);
                goto failed;
            }
            page_insert(mm->pgdir, page, addr, perm);
            swap_map_swappable(mm, addr, page, 1);
            page->pra_vaddr = addr;
        }
        else{
            cprintf("do_pgfault failed: swap_init_ok is 0 but ptep is %x\n",*ptep);
            goto failed;
        }
    }
    ret = 0;
failed:
    return ret;
}

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

先進先出演算法(FIFO)的思路是選擇在記憶體駐留時間最長的頁面進行替換,實現較為簡單,只需要維護一個記錄所有位於記憶體中的邏輯頁面連結串列,連結串列元素按駐留記憶體的時間排序,出現缺頁時選擇鏈首/鏈尾進行置換,新頁面加到鏈尾/鏈首

顯然FIFO演算法實現非常簡單,但是效能較差,隨著程序分配物理頁面數增加時缺頁甚至可能增加(BELADY現象),在ucore中簡單實現如下,位於kern/mm/swap_fifo.c

/*
 * (3)_fifo_map_swappable: According FIFO PRA, we should link the most recent arrival page at the back of pra_list_head queue
 */
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);
    //record the page access situation
    /*LAB3 EXERCISE 2: YOUR CODE*/ 
    //(1)link the most recent arrival page at the back of the pra_list_head queue.
    list_add_before(head, entry);
    return 0;
}
/*
 *  (4)_fifo_swap_out_victim: According FIFO PRA, we should unlink the  earliest arrival page in front of pra_list_head queue,
 *                            then set the addr of addr of this page to ptr_page.
 */
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);
     /* Select the victim */
     /*LAB3 EXERCISE 2: YOUR CODE*/ 
     //(1)  unlink the  earliest arrival page in front of pra_list_head queue
     //(2)  set the addr of addr of this page to ptr_page
     list_entry_t *le = head->next;
     //assert(head != le); //ref: labcode_answer...
     struct Page *page = le2page(le, pra_page_link);
     list_del(le);
     //assert(p != NULL);  //ref: labcode_answer...
     *ptr_page = page;
     return 0;
}

總結

完成所有內容後,呼叫make qemu 後執行結果如下,函式功能實現成功

check_vma_struct() succeeded!
page fault at 0x00000100: K/W [no page found].
check_pgfault() succeeded!                                
check_vmm() succeeded.
ide 0:      10000(sectors), 'QEMU HARDDISK'.
ide 1:     262144(sectors), 'QEMU HARDDISK'.
SWAP: manager = fifo swap manager
BEGIN check_swap: count 31966, total 31966
setup Page Table for vaddr 0X1000, so alloc a page
setup Page Table vaddr 0~4MB OVER!
set up init env for check_swap begin!
page fault at 0x00001000: K/W [no page found].
page fault at 0x00002000: K/W [no page found].
page fault at 0x00003000: K/W [no page found].
page fault at 0x00004000: K/W [no page found].
set up init env for check_swap over!
write Virt Page c in fifo_check_swap
write Virt Page a in fifo_check_swap
write Virt Page d in fifo_check_swap
write Virt Page b in fifo_check_swap
write Virt Page e in fifo_check_swap
page fault at 0x00005000: K/W [no page found].
swap_out: i 0, store page in vaddr 0x1000 to disk swap entry 2
write Virt Page b in fifo_check_swap
write Virt Page a in fifo_check_swap
page fault at 0x00001000: K/W [no page found].
swap_out: i 0, store page in vaddr 0x2000 to disk swap entry 3
swap_in: load disk swap entry 2 with swap_page in vadr 0x1000
write Virt Page b in fifo_check_swap
page fault at 0x00002000: K/W [no page found].
swap_out: i 0, store page in vaddr 0x3000 to disk swap entry 4
swap_in: load disk swap entry 3 with swap_page in vadr 0x2000
write Virt Page c in fifo_check_swap
page fault at 0x00003000: K/W [no page found].
swap_out: i 0, store page in vaddr 0x4000 to disk swap entry 5
swap_in: load disk swap entry 4 with swap_page in vadr 0x3000
write Virt Page d in fifo_check_swap
page fault at 0x00004000: K/W [no page found].
swap_out: i 0, store page in vaddr 0x5000 to disk swap entry 6
swap_in: load disk swap entry 5 with swap_page in vadr 0x4000
write Virt Page e in fifo_check_swap
page fault at 0x00005000: K/W [no page found].
swap_out: i 0, store page in vaddr 0x1000 to disk swap entry 2
swap_in: load disk swap entry 6 with swap_page in vadr 0x5000
write Virt Page a in fifo_check_swap
page fault at 0x00001000: K/R [no page found].
swap_out: i 0, store page in vaddr 0x2000 to disk swap entry 3
swap_in: load disk swap entry 2 with swap_page in vadr 0x1000
count is 7, total is 7
check_swap() succeeded!                            
++ setup timer interrupts
100 ticks
100 ticks
100 ticks

相關推薦

作業系統實驗實驗報告

實驗三:虛擬記憶體管理 練習0:填寫已有實驗 參考實驗二中的做法,使用meld可以很快完成 練習1:給未被對映的地址對映上物理頁 1.1 ucore虛擬記憶體機制 ucore的虛擬記憶體管理總體框架主要包含 完成初始化虛擬記

2017-2018-1 20155332盛照宗 實驗 實時系統報告

rcp ip add 程序 backlog ets clu ucc 文本 eof 20155332 實驗三 任務一: 1.學習使用Linux命令wc(1) 2.基於Linux Socket程序設計實現wc(1)服務器(端口號是你學號的後6位)和客戶端 3.

2017-2018-2 20165228 實驗《敏捷開發與XP實踐》實驗報告

們的 person pla 顯示器 乘除 執行 可靠 鍵盤輸入 ava 2017-2018-2 20165228 實驗三《敏捷開發與XP實踐》實驗報告 相關知識點 (一)敏捷開發與XP 通過 XP準則來表達: 溝通 :XP認為項目成員之間的溝通是項目成功的關鍵,並把溝通看

2017-2018-2 20165306 實驗《敏捷開發與XP實踐》實驗報告

The set mov 常見 DApp 兩個 如何 current head 實驗三《敏捷開發與XP實踐》實驗報告 實驗報告封面 實驗內容 XP基礎 XP核心實踐 相關工具 實驗步驟 (一) 敏捷開發與XP實踐-1 實驗要求: 參考 代碼規範 安裝alibaba 插件

2017-2018-2 20165327 實驗《敏捷開發與XP實踐》實驗報告

vim編輯器 linu 工具 敏捷開發 博客 其他 linux基礎入門 tro 指導 2017-2018-2 20165327 實驗三《敏捷開發與XP實踐》實驗報告 實驗三 《敏捷開發與XP實踐》 一、實驗報告封面 課程:Java程序設計 班級:1653 姓名:楊靖濤 學號

20165339 實驗《敏捷開發與XP實踐》實驗報告

至少 sse 開發 des 格式 arch iba 包含 logs 實驗步驟 參考http://www.cnblogs.com/rocedu/p/4795776.html, Eclipse的內容替換成IDEA 參考 http://www.cnblogs.com/roce

2017-2018-2 20165303 實驗《Java面向對象程序設計》實驗報告

junit單元測試 buffer 修改 print eclipse string pen OS best 實驗三 敏捷開發與XP實踐-1 實驗要求 實驗三 敏捷開發與XP實踐 http://www.cnblogs.com/rocedu/p/4795776.html, Ec

2017-2018-2 20165331 實驗《敏捷開發與XP實踐》實驗報告

練習 9.png 自己 lex tel 格式 HR com 程序設計 實驗三 敏捷開發與XP實踐 實驗報告封面 課程:Java程序設計 班級:1653 姓名:胡麟 學號:20165331 成績: 指導教師:婁嘉鵬 實驗日期:2018.4.28 實驗密級: 預習程度: 實驗時

20172313 2017-2018-2 《程序設計與數據結構》實驗報告

正常 標準 基本 密碼學 集體 遇到 閱讀 mage 這一 20172313 2017-2018-2 《程序設計與數據結構》實驗三報告 課程:《程序設計與數據結構》 班級: 1723 姓名: 余坤澎 學號:20172313 實驗教師:王誌強 實驗日期:2018年5月10日

2017-2018-2 《程序設計與數據結構》實驗報告

出現 資料 subject 同學 stringbu i++ new inf AR 學號 2017-2018-2 《程序設計與數據結構》實驗三報告 課程:《程序設計與數據結構》 班級: 1723 姓名: 康皓越 學號:20172326 實驗教師:王誌強 實驗日期:2018年5

實驗報告實驗

字節 每次 分享圖片 成對 png 偏移 過程 依次 代碼 練習一 assume cs:codecode segment mov ah,2 mov dl,3 add dl,30h int 21h mov

實驗報告

重新 屏幕 ls 命令 算術運算 osb 並保存 實驗 14. 計算機 學院 計軟院 專業 計算機科學與技術 年級 2017 級 班次 4班 姓名 任心怡 學號 20171308152 一、實驗目的 1. 掌握匯編語言源程序(8086 dos 匯編)編寫→匯編

20172311 2018-2019-1《程式設計與資料結構》實驗報告

20172311 2018-2019-1《程式設計與資料結構》實驗三報告 課程:《程式設計與資料結構》 班級: 1723 姓名: 趙曉海 學號:20172311 實驗教師:王志強 實驗日期:2018年11月19日 必修/選修: 必修 1.實驗內容 實驗三-查詢與排序-1 定義一個Searching

20172315 2018-2019-1 《程式設計與資料結構》實驗報告

20172315 2018-2019-1 《程式設計與資料結構》實驗三報告 課程:《程式設計與資料結構》 班級: 1723 姓名: 胡智韜 學號:20172315 實驗教師:王志強 實驗日期:2018年11月19日 必修/選修: 必修 1.實驗內容 實驗三-查詢與排序-1 定義一個Searching和

20172329 2018-2019 《Java軟體結構與資料結構》實驗報告

20172329 2018-2019-2 《Java軟體結構與資料結構》實驗三報告 課程:《Java軟體結構與資料結構》 班級: 1723 姓名: 王文彬 學號:20172329 實驗教師:王志強 實驗日期:2018年11月19日 必修/選修: 必修 一、實驗內容 1.1 第一個實驗內容

《程式設計與資料結構》實驗報告

學號 2017-2018-2 《程式設計與資料結構》實驗三報告 課程:《程式設計與資料結構》 班級: 1723 姓名: 康皓越 學號:20172326 實驗教師:王志強 實驗日期:2018年11月19日 必修/選修: 必修 1.實驗內容 實驗1: 定義一個Searching和Sorti

20172304 實驗報告

20172304 實驗二報告 課程:《軟體結構與資料結構》 班級: 1723 姓名: 段志軒 學號:20172304 實驗教師:王志強 助教:張師瑜&張之睿 實驗日期:2018年11月5日-2018年11月12日 必修選修: 必修 實驗要求 實驗三-查詢與排序-1

20172308 實驗《程序設計與數據結構》查找與排序 實驗報告

sorting 折半 相關 -m 缺少 新建 找不到 png 堆排 20172308 2018-2019-1 實驗3 《查找與排序》報告 課程:《程序設計與數據結構》 班級: 1723 姓名: 周亞傑 學號:20172308 實驗教師:王誌強 實驗日期:2018年10月20

20172303 2018-2019-1 《程序設計與數據結構》實驗報告

get end tree equals ner 後者 else if ide second 20172303 2018-2019-1 《程序設計與數據結構》實驗三報告 課程:《程序設計與數據結構》 班級: 1723 姓名: 範雯琪 學號:20172303 實驗教師:王誌

20172324 2018-2019-1《程式設計與資料結構》實驗報告

20172324 2018-2019-1《程式設計與資料結構》實驗三報告 課程:《程式設計與資料結構》 班級: 1723 姓名: 曾程 學號:20172324 實驗教師:王志強 實驗日期:2018年11月19日 必修/選修: 必修 一、實驗內容 連結串列練習 實驗一:定義一個Searching和