操作系統實驗ucore lab3

閱讀前注意事項:

1、我的博客從lab2之後,如果沒有特殊說明,所有標註的代碼行數位置,以labcodes_answer(答案包)裏的文件爲準!!!因爲你以後會發現做實驗用meld軟件比較費時費力,對於咱們學校的驗收不如直接對着答案來;

2、感謝網上的各路前輩大佬們,本人在這學期初次完成實驗的過程中,各位前輩們的博客給了我很多有用的指導;本人的博客內容在現有的內容上,做了不少細節的增補內容,有些地方屬個人理解,如果有錯在所難免,還請各位大佬們批評指正;

3、所有實驗的思考題,我把它規整到了文章最後;

4、所有實驗均默認不做challenge,對實驗評分無影響。

 

一、實驗內容

本次實驗是在實驗二的基礎上, 藉助於頁表機制和實驗一中涉及的中斷異常處理機制, 完成Page Fault異常處理和FIFO頁替換算法的實現。 實驗原理最大的區別是在設計瞭如何在磁盤上緩存內存頁, 從而能夠支持虛存管理, 提供一個比實際物理內存空間“更大”的虛擬內存空間給系統使用。

 

二、目的

瞭解虛擬內存的Page Fault異常處理實現;

瞭解頁替換算法在操作系統中的實現。

 

三、實驗設計思想和流程

 

練習0:填寫已有實驗

 

本實驗依賴實驗1/2。請把你做的實驗1/2的代碼填入本實驗中代碼中有“LAB1”、“LAB2”的註釋相應部分。

經過比較,需要更改的文件爲:

 

kdebug.c

trap.c

default_pmm.c

pmm.c

 

其餘地方無需修改,可以直接使用。

 

練習1:給未被映射的地址映射上物理頁(需要編程)

 

完成 do_pgfault(mm/vmm.c)函數,給未被映射的地址映射上物理頁。設置訪問權限的時候需要參考頁面所在VMA的權限,同時需要注意映射物理頁時需要操作內存控制結構所制定的頁表,而不是內核的頁表。

 

什麼是虛擬內存?簡單地說是指程序員或CPU“看到”的內存。但有幾點需要注意:

 

1. 虛擬內存單元不一定有實際的物理內存單元對應,即實際的物理內存單元可能不存在;

2. 如果虛擬內存單元對應有實際的物理內存單元,那二者的地址一般是不相等的;

3. 通過操作系統實現的某種內存映射可建立虛擬內存與物理內存的對應關係,使得程序員或CPU訪問的虛擬內存地址會自動轉換爲一個物理內存地址。

 

那麼這個“虛擬”的作用或意義在哪裏體現呢?在操作系統中,虛擬內存其實包含多個虛擬層次,在不同的層次體現了不同的作用。首先,在有了分頁機制後,程序員或CPU“看到”的地址已經不是實際的物理地址了,這已經有一層虛擬化,我們可簡稱爲內存地址虛擬化。有了內存地址虛擬化,我們就可以通過設置頁表項來限定軟件運行時的訪問空間,確保軟件運行不越界,完成內存訪問保護的功能。

 

通過內存地址虛擬化,可以使得軟件在沒有訪問某虛擬內存地址時不分配具體的物理內存,而只有在實際訪問某虛擬內存地址時,操作系統再動態地分配物理內存,建立虛擬內存到物理內存的頁映射關係,這種技術稱爲按需分頁(demand paging)。把不經常訪問的數據所佔的內存空間臨時寫到硬盤上,這樣可以騰出更多的空閒內存空間給經常訪問的數據;當CPU訪問到不經常訪問的數據時,再把這些數據從硬盤讀入到內存中,這種技術稱爲頁換入換出(page swap in/out)。這種內存管理技術給了程序員更大的內存“空間”,從而可以讓更多的程序在內存中併發運行。

 

do_pgfault的功能是給未被映射的(虛擬)地址映射上物理頁,那麼它和頁面異常是一種什麼樣的關係?

 

首先需要了解虛擬地址存在的意義:

 

之所以要創建一個虛擬地址空間,目的是爲了解決進程地址空間隔離的問題。但程序要想執行,必須運行在真實的內存上,所以,必須在虛擬地址與物理地址間建立一種映射關係。這樣,通過映射機制,當程序訪問虛擬地址空間上的某個地址值時,就相當於訪問了物理地址空間中的另一個值。虛擬地址存在的意義,相當於是提供一個比實際物理內存空間“更大”的虛擬內存空間給系統使用。

 

通過內存地址虛擬化,可以使得軟件在沒有訪問某虛擬內存地址時不分配具體的物理內存,而只有在實際訪問某虛擬內存地址時,操作系統再動態地分配物理內存,建立虛擬內存到物理內存的頁映射關係,這種技術稱爲按需分頁(demand paging)。

 

當啓動分頁機制以後,如果一條指令或數據的虛擬地址所對應的物理頁框不在內存中或者訪問的類型有錯誤(比如寫一個只讀頁或用戶態程序訪問內核態的數據等),就會發生頁錯誤異常。產生頁面異常的原因主要有:

 

(1)目標(虛擬)頁面不存在(頁表項全爲0,即該線性地址與物理地址尚未建立映射或者已經撤銷),此時應該直接報錯;

 

(2)相應的物理頁面不在內存中(頁表項非空,但Present標誌位=0,比如在swap分區或磁盤文件上),此時應該建立映射虛擬頁和物理頁的映射關係;

 

(3)目標訪問頁的權限不符合(此時頁表項P標誌=1,比如企圖寫只讀頁面),此時應該直接報錯。

 

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

一塊虛擬地址(vma)可能映射到一個或多個多個物理頁。

 

由於一開始,分配虛擬空間超過了物理空間的大小,比如在上圖中只有5個物理頁幀,但其實我們給它分了7個虛擬頁,那很明顯一定會有兩個虛擬頁沒有對應的物理頁幀。

 

那麼如果訪問到這兩個虛擬頁,在二級頁表裏面它會沒有對應映射關係,一旦沒有對應映射關係,那就會產生缺頁異常,因此就是do_pgfault函數的實現功能,它要建立這個映射關係。

 

一旦產生缺頁異常,會出現一個異常的中斷,此時經過前兩個實驗中的中斷處理過程,一直調用了do_pgfault的具體實現。

下圖反映了該過程的調用關係:

那麼,下面開始首先是關鍵的數據結構的解釋:

 

1、虛擬內存塊空間(vma,virtual memory area,kern/mm/vmm.h,12——19行)

// the virtual continuous memory area(vma)
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
    uint32_t vm_flags;       		// flags of vma
    list_entry_t list_link;  	       // linear list link which sorted by start addr of vma
};

VMA數據結構,用於管理應用程序的虛擬內存,更準確來說,VMA是描述應用程序對虛擬內存“需求”的結構,它包含五個成員:

 

mm:是一個更高級更抽象的數據結構,代表整個應用程序所用到的所有VMA(該VMA的佔有程序),接下來馬上討論。

 

vm_start、vm_end:描述一個合理的地址空間範圍(確保 vm_start < vm_end的關係);

 

vm_flags:該虛擬內存空間的屬性,目前的屬性包括可讀、可寫、可執行;

它們被定義在(kern/mm/vmm.h,24——26行):

#define VM_READ                	0x00000001
#define VM_WRITE              	0x00000002
#define VM_EXEC               	0x00000004
//可以看出來三個權限是在uint32的後三位上。

list_link:一個雙向鏈表,按照從小到大的順序把一系列用vma_struct表示的虛擬內存空間鏈接起來,並且還要求這些鏈起來的vma_struct應該是不相交的,即vma之間的地址空間無交集。

 

2、應用程序映射到的虛擬空間塊綜述結構mm(kern/mm/vmm.h,28——35行)

// 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
};

如果說vma描述和管理的是一系列虛擬內存塊結構,那麼mm則用來描述,究竟是哪個應用程序使用了這些vma,它們是通過vma中的成員mm聯繫在一起的。

 

mm也有五個成員,分別解釋如下:

 

list_entry_t mmap_list:雙向鏈表,鏈接了所有屬於同一頁目錄表的虛擬內存空間;

 

struct vma_struct *mmap_cache:指向當前正在使用的虛擬內存空間;

 

pde_t *pgdir:指向的mm_struct數據結構所維護的一級頁表,每個應用程序都有頁表,那麼這裏指向應用程序正在操作的那個;

 

int map_count:記錄mmap_list裏鏈接的vma_struct的個數(該程序用了多少個虛擬頁);

 

void *sm_priv:指向用來鏈接記錄頁訪問情況的鏈表頭。用於FIFO替換策略的訪問。

 

那麼,接下來討論頁訪問異常的調用關係過程:

1、彙編代碼中Vector.S中,當出現了一個頁訪問錯誤的時候,會得到一個錯誤碼14,經過一系列處理之後保存爲中斷狀態指針tf,交給trap處理;

 

2、trap函數:(kern/trap/trap.c,231+行):

void
trap(struct trapframe *tf) {
    // dispatch based on what type of trap occurred
    trap_dispatch(tf);
}

直接調用trap_dispatch函數進行下一步處理。

 

3、trap_dispatch函數:(kern/trap/trap.c,只關心 172——183行)

trap_dispatch(struct trapframe *tf) {
    char c;
    int ret;

    switch (tf->tf_trapno) {//中斷類型
    case T_PGFLT:  //page fault
        if ((ret = pgfault_handler(tf)) != 0) {
            print_trapframe(tf);
            panic("handle pgfault failed. %e\n", ret);
        }
        break;
……

這是實驗一中的中斷處理函數,操作系統中,中斷產生的原因很多。

 

傳入參數是一箇中斷指針tf,接下來會對於這個中斷指針進行switch進行判斷,確認一下在各種中斷情況中,它具體是由於什麼情況中斷的,這裏我到的中斷原因是T_PGFLT,即頁訪問錯誤,那麼我們會調用一個函數pgfault_handler函數去進一步處理頁訪問錯誤的情況。

 

4、pgfault_handler(kern/trap/trap.c,158——166行)

static int
pgfault_handler(struct trapframe *tf) {
    extern struct mm_struct *check_mm_struct;
    print_pgfault(tf);
    if (check_mm_struct != NULL) {
        return do_pgfault(check_mm_struct, tf->tf_err, rcr2());   
//cr2是產生錯誤的虛擬地址
    }
    panic("unhandled page fault.\n");
}

這裏做的事情並不多,主要就是調用了do_pgfault函數來返回一個頁錯誤處理結果。

 

5、do_pgfault(需實現函數,kern/vmm.c,304+行)

 

函數實現如下:

int
do_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr) {
    int ret = -E_INVAL;
struct vma_struct *vma = find_vma(mm, addr); 	
//查詢vma,判斷addr是否在vma中

    pgfault_num++;
    if (vma == NULL || vma->vm_start > addr) { 	//vma->vm_start<=addr end
        cprintf("not valid addr %x, and  can not find it in vma\n", addr);
        goto failed;
}

    switch (error_code & 3) {
    default:
    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:
        cprintf("do_pgfault failed: error code flag = read AND present\n");
        goto failed;	//讀取的問題,直接報錯,因爲一般可讀
    case 0:
        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;
        }
    }
    uint32_t perm = PTE_U;         //perm是給物理頁賦予權限的中間變量
    if (vma->vm_flags & VM_WRITE) {
        perm |= PTE_W;
    }
    addr = ROUNDDOWN(addr, PGSIZE);
    ret = -E_NO_MEM;
    pte_t *ptep=NULL;

    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; 
            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); 	//建立虛擬地址和物理地址之間的對應關係,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;
        }
   }
   ret = 0;
failed:
    return ret;
}

該函數主要完成的是具體的頁訪問異常處理工作:

 

首先是三個傳入參數,它們分別是:應用程序虛擬存儲總管mm、錯誤碼error_code和具體出錯的虛擬地址addr。

 

接下來,我們討論對於具體可能出現頁訪問錯誤的情況進行一一處理和報錯。

 

(1)如果是虛擬地址的問題,如虛擬地址的範圍超過了限制,或者是虛擬地址無法被查找到,即可以說該地址是不合法的,進行了一次非法訪問,那麼可以直接報錯。

 

(2)如果是目標訪問頁的權限不符合,比如對一個只讀頁進行寫操作,或者讀了一個不可讀的頁,那麼此時可以直接報錯。

 

這裏涉及到了一個對於傳入的錯誤碼error_code的理解,在該函數的註釋中,我們瞭解到,錯誤碼的低3位分別是:

 

P標誌(位0)最低位:表示當前的錯誤是由於不存在頁面(0)引起,還是由於違反訪問權限(1)引起。

 

W / R標誌(位1):表示當前錯誤是由於讀操作(0)引起還是還是寫操作(1)引起。

 

因此,和權限相關的判斷,只需要對P和W/R,即error_code的最低兩位判斷即可。

如果能夠順利通過上述的合法性判斷,那麼此次虛擬內存訪問就被認爲是合法的,此時,頁訪問異常的原因,是由於該合法虛擬頁,沒有對應物理頁的映射導致,因此下一步要建立起這個映射。

 

建立起這個映射首先要改變一下待換入頁面的權限值:

 

PTE_W          0x002    // page table/directory entry flags bit : Writeable

PTE_U          0x004     // page table/directory entry flags bit : User can access

 

首先,無論如何這個頁面需要能夠被用戶訪問,其次,如果對應映射的vma有寫的權限,該物理頁也需要可寫。

 

然後,我們通過當前應用程序mm所指向的一級頁表,以及虛擬地址,去查詢有沒有對應的二級頁表,如果查詢結果爲NULL,那麼報錯,因爲沒有對應的二級頁表項,它根本不存在,也不知道用什麼物理頁去映射(當然,這裏不可能不存在,如果查找到不存在的情況,由於get_pte的create標記位爲1,那麼會創建一個新的二級頁表);

 

之後,如果是上述新創建的二級頁表,那麼*ptep就會是0,代表頁表爲空,此時調用pgdir_alloc_page,對它進行初始化;接下來的else語句就是對它進行一個映射的替換:

 

swap_init_ok是一個標記位,代表交換初始化成功,可以開始替換的過程了,

 

首先聲明瞭一個頁,之後將結構mm、虛擬地址和這個空頁,調用了swap_in函數

(kern/mm/swap.c,119——136行)

int
swap_in(struct mm_struct *mm, uintptr_t addr, struct Page **ptr_result)//addr是出錯地址
//page是新找到的頁
{
     struct Page *result = alloc_page();
     assert(result!=NULL);

     pte_t *ptep = get_pte(mm->pgdir, addr, 0);
     // cprintf("SWAP: load ptep %x swap entry %d to vaddr 0x%08x, page %x, No %d\n", ptep, (*ptep)>>8, addr, result, (result-pages));
    
     int r;
     if ((r = swapfs_read((*ptep), result)) != 0)
     {
        assert(r!=0);
     }
     cprintf("swap_in: load disk swap entry %d with swap_page in vadr 0x%x\n", (*ptep)>>8, addr);
     *ptr_result=result;
     return 0;
}

該函數首先爲傳入的空頁page分配初始化,之後獲取了mm一級頁表對應的二級頁表,通過swapfs_read嘗試將硬盤中的內容換入到新的page中,即可返回,最後的return 0是一個正常返回值。

 

執行完上述函數,最後,建立起該頁的虛擬地址和物理地址之間的對應關係,然後設置爲可交換,該頁的虛擬地址設置爲傳入的地址。至此,do_pgfault結束,建立起了新的映射關係,下次訪問不會有異常。

 

最後,將練習1總結如下:

 

(1)do_pgfault函數的輸入是一個出錯的應用程序、錯誤碼、出錯的虛擬地址;

(2)通過虛擬地址和錯誤碼權限的判斷,可以確認是否可以直接報錯;

(3)如果數據都是合法的,那麼需要建立映射關係;

(4)如果虛擬地址所映射的物理頁已經存在,那麼新加入映射關係即可,如果不存在需要另外創建。

 

練習2:補充完成基於FIFO的頁面替換算法(需要編程)

 

完成vmm.c中的do_pgfault函數,並且在實現FIFO算法的swap_fifo.c中完map_swappable和swap_out_victim函數。通過對swap的測試。注意:在LAB2 EXERCISE 2處填寫代碼。

 

操作系統爲何要進行頁面置換呢?這是由於操作系統給用戶態的應用程序提供了一個虛擬的“大容量”內存空間,而實際的物理內存空間又沒有那麼大。所以操作系統就就“瞞着”應用程序,只把應用程序中“常用”的數據和代碼放在物理內存中,而不常用的數據和代碼放在了硬盤這樣的存儲介質上。

 

因此,在練習1中,當頁錯誤異常發生時,有可能是因爲頁面保存在swap區或者磁盤文件上造成的,所以我們需要利用頁面替換算法解決這個問題。

 

而練習一和這裏的關聯就在於:頁面替換主要分爲兩個方面,頁面換出和頁面換入。練習一實現的是頁面換入,主要在上述的do_pgfault()函數實現;而練習二這裏主要實現,頁面換出,主要是在swap_out_vistim()函數。另外,在練習一還有一個函數叫做swappable,代表將該頁面設置爲可交換的。於是,練習二主要是對於這兩個函數的實現。

 

實現頁面的替換需要換入和換出,這裏使用的是FIFO策略:

 

先進先出(First In First Out, FIFO)頁替換算法:該算法總是淘汰最先進入內存的頁,即選擇在內存中駐留時間最久的頁予以淘汰。只需把一個應用程序在執行過程中已調入內存的頁按先後次序鏈接成一個隊列,隊列頭指向內存中駐留時間最久的頁,隊列尾指向最近被調入內存的頁。這樣需要淘汰頁時,從隊列頭很容易查找到需要淘汰的頁。

 

下面是具體實現部分,頁調度算法也是基於框架的,該框架定義在

(kern/mm/swap.h,33——51行)

struct swap_manager
{
     const char *name;
     /* Global initialization for the swap manager */
     int (*init)            (void);
     /* Initialize the priv data inside mm_struct */
     int (*init_mm)         (struct mm_struct *mm);
     /* Called when tick interrupt occured */
     int (*tick_event)      (struct mm_struct *mm);
     /* Called when map a swappable page into the mm_struct */
     int (*map_swappable)   (struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in);
     /* When a page is marked as shared, this routine is called to
      * delete the addr entry from the swap manager */
     int (*set_unswappable) (struct mm_struct *mm, uintptr_t addr);
     /* Try to swap out a page, return then victim */
     int (*swap_out_victim) (struct mm_struct *mm, struct Page **ptr_page, int in_tick);
     /* check the page relpacement algorithm */
     int (*check_swap)(void);     
};

都是成員函數,通過之前一樣的方法來進行綁定,這樣就能執行一套完整的算法。

 

在實驗二中,物理頁的數據結構在這裏也有所改動:(kern/mm/memlayout.h,100——107行)

struct Page {
    int ref;                  	// page frame's reference counter
    uint32_t flags;          	// array of flags that describe the status of the page frame
    unsigned int property;    	// the num of free block, used in first fit pm manager
    list_entry_t page_link;    	// free list link
    list_entry_t pra_page_link;  	// used for pra (page replace algorithm)
uintptr_t pra_vaddr;      	// used for pra (page replace algorithm)
//未命中的虛擬地址
};

主要是加入了一個雙向鏈表pra_page_link,用於記錄加入內存順序這方面的內容。pra_page_link可用來構造按頁的第一次訪問時間進行排序的一個鏈表,這個鏈表的開始表示第一次訪問時間最近的頁,鏈表結尾表示第一次訪問時間最遠的頁。當然鏈表頭可以就可設置爲pra_list_head(定義在swap_fifo.c中),構造的時機是在page fault發生後,進行do_pgfault函數時。pra_vaddr可以用來記錄此物理頁對應的虛擬地址。

 

首先來看FIFO的初始化(kern/swap_fifo.c,33——40行):

_fifo_init_mm(struct mm_struct *mm)
{     
     list_init(&pra_list_head);
     mm->sm_priv = &pra_list_head;
     //cprintf(" mm->sm_priv %x in fifo_init_mm\n",mm->sm_priv);
     return 0;
}

首先,沒有頁在內存中,因此將列表清空,表頭指向隊列最開始。

 

那麼,調用了swappable函數之後,執行的是一個頁加入隊列的操作,由page fault觸發

(kern/swap_fifo.c,44——56行)

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 situlation
    /*LAB3 EXERCISE 2: YOUR CODE*/ 
    //(1)link the most recent arrival page at the back of the pra_list_head qeueue.
    list_add(head, entry);
    return 0;
}

我們看到,符合算法的定義,每次都在表頭加入新的page。

 

接下來,就是換出的策略,FIFO換出最早進入內存的頁面,也就是在隊尾,該函數的位置是(kern/swap_fifo.c,61——79行)

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 qeueue
     //(2)  set the addr of addr of this page to ptr_page
     /* Select the tail */
     list_entry_t *le = head->prev;
     assert(head!=le);
     struct Page *p = le2page(le, pra_page_link);
     list_del(le);
     assert(p !=NULL);
     *ptr_page = p;
     return 0;
}

在替換出頁面的時候,使用鏈表操作,刪除掉最早進入的那個頁,並按照註釋將這個頁面給到傳入參數ptr_page。

 

四、思考題

 

思考題1:描述頁目錄項(Pag Director Entry)和頁表(Page Table Entry)中組成部分對ucore實現頁替換算法的潛在用處。

 

答:分頁機制的實現,確保了虛擬地址和物理地址之間的對應關係,一方面,通過查找虛擬地址是否存在於一二級頁表中,可以容易發現該地址是否是合法的,另一方面,通過修改映射關係即可實現頁替換操作。

 

另外,基於頁表實現了地址的分段操作,在這裏,一個物理地址不同的位數上,會存儲一系列不同的信息,比如,pg_fault函數中的權限判斷就用到了這方面的操作,觀察代碼中的宏定義,我們得到部分標記位的含義如下:

#define PTE_P       0x001          // Present 對應物理頁面是否存在

#define PTE_W       0x002          // Writeable 對應物理頁面是否可寫

#define PTE_U       0x004          // User 對應物理頁面用戶態是否可以訪問

#define PTE_PWT     0x008          // Write-Through 對應物理頁面在寫入時是否寫透(即向更低級儲存設備寫入)

#define PTE_PCD     0x010          //Cache-Disable 對應物理頁面是否能被放入高速緩存

#define PTE_A       0x020          // Accessed 對應物理頁面是否被訪問

#define PTE_D       0x040          // Dirty 對應物理頁面是否被寫入

#define PTE_PS      0x080          // Page Size 對應物理頁面的頁面大小

#define PTE_MBZ     0x180          // Bits must be zero 必須爲零的部分

#define PTE_AVAIL   0xE00          // Available for software use 用戶可自定義的部分

 

思考題2:如果ucore的缺頁服務例程在執行過程中訪問內存,出現了頁訪問異常,請問硬件要做哪些事情?

 

答:CPU會把產生異常的線性地址存儲在CR2寄存器中,並且把表示頁訪問異常類型的值(簡稱頁訪問異常錯誤碼,errorCode)保存在中斷棧中。之後通過上述分析的trap–> trap_dispatch–>pgfault_handler–>do_pgfault調用關係,一步步做出處理。

 

思考題3:如果要在ucore上實現"extended clock頁替換算法"請給你的設計方案,現有的swap_manager框架是否足以支持在ucore中實現此算法?如果是,請給你的設計方案。如果不是,請給出你的新的擴展和基此擴展的設計方案。並需要回答如下問題:

 

需要被換出的頁的特徵是什麼?

在ucore中如何判斷具有這樣特徵的頁?

何時進行換入和換出操作?

 

時鐘(Clock)頁替換算法:是LRU算法的一種近似實現。時鐘頁替換算法把各個頁面組織成環形鏈表的形式,類似於一個鐘的表面。然後把一個指針(簡稱當前指針)指向最老的那個頁面,即最先進來的那個頁面。另外,時鐘算法需要在頁表項(PTE)中設置了一位訪問位來表示此頁表項對應的頁當前是否被訪問過。當該頁被訪問時,CPU中的MMU硬件將把訪問位置“1”。當操作系統需要淘汰頁時,對當前指針指向的頁所對應的頁表項進行查詢,如果訪問位爲“0”,則淘汰該頁,如果該頁被寫過,則還要把它換出到硬盤上;如果訪問位爲“1”,則將該頁表項的此位置“0”,繼續訪問下一個頁。該算法近似地體現了LRU的思想,且易於實現,開銷少,需要硬件支持來設置訪問位。時鐘頁替換算法在本質上與FIFO算法是類似的,不同之處是在時鐘頁替換算法中跳過了訪問位爲1的頁。

 

解:目前的swap_manager框架足以支持在ucore中實現extended clock算法,之所以能夠支持該算法,是因爲在kern/mm/mmu.h文件中有如下定義

#define PTE_P       0x001        // Present 對應物理頁面是否存在

#define PTE_W       0x002        // Writeable 對應物理頁面是否可寫

#define PTE_U       0x004        // User 對應物理頁面用戶態是否可以訪問

#define PTE_PWT     0x008        // Write-Through 對應物理頁面在寫入時是否寫透(即向更低級儲存設備寫入)

#define PTE_PCD     0x010        //Cache-Disable 對應物理頁面是否能被放入高速緩存

#define PTE_A       0x020        // Accessed 對應物理頁面是否被訪問

#define PTE_D       0x040        // Dirty 對應物理頁面是否被寫入

#define PTE_PS      0x080        // Page Size 對應物理頁面的頁面大小

#define PTE_MBZ     0x180        // Bits must be zero 必須爲零的部分

#define PTE_AVAIL   0xE00        // Available for software use 用戶可自定義的部分

其中PTE_A中的內容的即標誌着該頁是否被訪問過,由此我們可以對kern/mm/swap_fifo.c做相應的修改,判斷是否被訪問過即可

 

需要被換出的頁的特徵是:最早被換入,且最近沒有被訪問過的頁。

 

在ucore中如何判斷具有這樣特徵的頁:首先判斷其最近有沒有被訪問過(利用位運算,讓條件*ptep & PTE_A是否爲1),若無,則按照FIFO原則進行置換。

 

何時進行換入和換出操作:類似於FIFO算法,當需要調用的頁不在頁表中時,並且在頁表已滿的情況下,需要進行換入和換出操作。

 

五、運行結果

1、qemu中出現頁表信息:

2、如果一切正常,得分應當是45/45:

 

發佈了35 篇原創文章 · 獲贊 43 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章