所有的實驗報告將會在 Github 同步更新,更多內容請移步至Github:https://github.com/AngelKitty/review_the_national_post-graduate_entrance_examination/blob/master/books_and_notes/professional_courses/operating_system/sources/ucore_os_lab/docs/lab_report/
練習0:填寫已有實驗
lab3
會依賴 lab1
和 lab2
,我們需要把做的 lab1
和 lab2
的代碼填到 lab3
中缺失的位置上面。練習 0 就是一個工具的利用。這裏我使用的是 Linux
下的系統已預裝好的 Meld Diff Viewer
工具。和 lab2
操作流程一樣,我們只需要將已經完成的 lab1
和 lab2
與待完成的 lab3
(由於 lab2
是基於 lab1
基礎上完成的,所以這裏只需要導入 lab2
)分別導入進來,然後點擊 compare
就行了。
然後軟件就會自動分析兩份代碼的不同,然後就一個個比較比較複製過去就行了,在軟件裏面是可以支持打開對比複製了,點擊 Copy Right
即可。當然 bin
目錄和 obj
目錄下都是 make
生成的,就不用複製了,其他需要修改的地方主要有以下四個文件,通過對比複製完成即可:
default_pmm.c
pmm.c
trap.c
kdebug.c
練習1:給未被映射的地址映射上物理頁(需要編程)
本實驗要求完成 do_pgfault
函數,作用給未被映射的地址映射上物理頁。
具體而言,當啓動分頁機制以後,如果一條指令或數據的虛擬地址所對應的物理頁框不在內存中或者訪問的類型有錯誤(比如寫一個只讀頁或用戶態程序訪問內核態的數據等),就會發生頁錯誤異常。產生頁面異常的原因主要有:
- 目標頁面不存在(頁表項全爲
0
,即該線性地址與物理地址尚未建立映射或者已經撤銷); - 相應的物理頁面不在內存中(頁表項非空,但
Present
標誌位=0
,比如在swap
分區或磁盤文件上) - 訪問權限不符合(此時頁表項
P
標誌=1
,比如企圖寫只讀頁面).
當出現上面情況之一,那麼就會產生頁面 page fault(#PF)
異常。產生異常的線性地址存儲在 CR2
中,並且將是 page fault
的產生類型保存在 error code
中。
因此此函數是完成頁錯誤異常處理的主要函數,它根據 CPU
的控制寄存器 CR2
中獲取的頁錯誤異常的虛擬地址,以及根據 error code
的錯誤類型來查找次虛擬地址是否在某個 VMA
的地址範圍內,並且是否滿足正確的讀寫權限。如果在此範圍內並且權限也正確,就認爲這是一次合法訪問,但沒有建立虛實對應關係,所以需要分配一個空閒的內存頁,並修改頁表完成虛地址到物理地址的映射,刷新 TLB
,然後調用 iret
中斷,返回並重新執行。如果該虛地址不在某 VMA
範圍內,這認爲是一個非法訪問。
那麼我們的這個 do_pgfault
函數的思路就明顯了。do_pgfault()
函數從 CR2
寄存器中獲取頁錯誤異常的虛擬地址,根據 error code
來查找這個虛擬地址是否在某一個 VMA
的地址範圍內,那麼就給它分配一個物理頁。
page_fault 函數不知道哪些是“合法”的虛擬頁,原因是 ucore
還缺少一定的數據結構來描述這種不在物理內存中的“合法”虛擬頁。爲此 ucore
通過建立 mm_struct
和 vma_struct
數據結構,描述了 ucore
模擬應用程序運行所需的合法內存空間。當訪問內存產生 page fault 異常時,可獲得訪問的內存的方式(讀或寫)以及具體的虛擬內存地址,這樣 ucore
就可以查詢此地址,看是否屬於 vma_struct
數據結構中描述的合法地址範圍中,如果在,則可根據具體情況進行請求調頁/頁換入換出處理;如果不在,則報錯。
虛擬地址空間和物理地址空間的示意圖如下圖所示:
這裏的 VMA 是描述應用程序對虛擬內存“需求”的變量,如下:
struct vma_struct {
struct mm_struct *vm_mm; //指向一個比 vma_struct 更高的抽象層次的數據結構 mm_struct
uintptr_t vm_start; //vma 的開始地址
uintptr_t vm_end; // vma 的結束地址
uint32_t vm_flags; // 虛擬內存空間的屬性
list_entry_t list_link; //雙向鏈表,按照從小到大的順序把虛擬內存空間鏈接起來
};
其中,各變量的屬性如下:
vm_start
和vm_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
。
struct mm_struct {
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; //指向用來鏈接記錄頁訪問情況的鏈表頭
};
其中,各變量的屬性如下:
mmap_list
是雙向鏈表頭,鏈接了所有屬於同一頁目錄表的虛擬內存空間。mmap_cache
是指向當前正在使用的虛擬內存空間,由於操作系統執行的“局部性”原理,當前正在用到的虛擬內存空間在接下來的操作中可能還會用到,這時就不需要查鏈表,而是直接使用此指針就可找到下一次要用到的虛擬內存空間。pgdir
所指向的就是mm_struct
數據結構所維護的頁表。通過訪問pgdir
可以查找某虛擬地址對應的頁表項是否存在以及頁表項的屬性等。map_count
記錄mmap_list
裏面鏈接的vma_struct
的個數。sm_priv
指向用來鏈接記錄頁訪問情況的鏈表頭,這建立了mm_struct
和後續要講到的swap_manager
之間的聯繫。
其結構關係如圖所示:
do_pgfault
的調用關係如下圖所示:
實現過程如下:(包含了練習 1 以及 練習 2 的部分實現)
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);//查詢 vma
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
*
*/
--------------------------------------------------------------------------------------------
* 設計思路:
首先檢查頁表中是否有相應的表項,如果表項爲空,那麼說明沒有映射過;
然後使用 pgdir_alloc_page 獲取一個物理頁,同時進行錯誤檢查即可。
/*LAB3 EXERCISE 1: YOUR CODE*/
// try to find a pte, if pte's PT(Page Table) isn't existed, then create a PT.
// (notice the 3th parameter '1')
if ((ptep = get_pte(mm->pgdir, addr, 1)) == NULL) {
cprintf("get_pte in do_pgfault failed\n");
goto failed;
}
//如果頁表不存在,嘗試分配一空閒頁,匹配物理地址與邏輯地址,建立對應關係
if (*ptep == 0) { // if the phy addr isn't exist, then alloc a page & map the phy addr with logical addr
if (pgdir_alloc_page(mm->pgdir, addr, perm) == NULL) { //失敗內存不夠退出
cprintf("pgdir_alloc_page in do_pgfault failed\n");
goto failed;
}
}
--------------------------------------------------------------------------------------------
/*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
*/
--------------------------------------------------------------------------------------------
* 設計思路:
如果 PTE 存在,那麼說明這一頁已經映射過了但是被保存在磁盤中,需要將這一頁內存交換出來:
1.調用 swap_in 將內存頁從磁盤中載入內存;
2.調用 page_insert 建立物理地址與線性地址之間的映射;
3.設置頁對應的虛擬地址,方便交換出內存時將正確的內存數據保存在正確的磁盤位置;
4.調用 swap_map_swappable 將物理頁框加入 FIFO。
/*LAB3 EXERCISE 2: YOUR CODE*/
//頁表項非空,嘗試換入頁面
else { // if this pte is a swap entry, then load data from disk to a page with phy addr
// and call page_insert to map the phy addr with logical addr
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;
}
}
ret = 0;
failed:
return ret;
}
請描述頁目錄項(Page Directory Entry)和頁表項(Page Table Entry)中組成部分對 ucore 實現頁替換算法的潛在用處。
表項中 PTE_A
表示內存頁是否被訪問過,PTE_D
表示內存頁是否被修改過,藉助着兩位標誌位可以實現 Enhanced Clock 算法。
如果 ucore 的缺頁服務例程在執行過程中訪問內存,出現了頁訪問異常,請問硬件要做哪些事情?
如果出現了頁訪問異常,那麼硬件將引發頁訪問異常的地址將被保存在 cr2
寄存器中,設置錯誤代碼,然後觸發 Page Fault
異常。
練習2:補充完成基於 FIFO 的頁面替換算法(需要編程)
本實驗要求完成 do_pgfault
函數,並且在實現 FIFO
算法的 swap_fifo.c
中完成 map_swappable
和 swap_out_victim
函數,通過對 swap
的測試。根據練習 1,當頁錯誤異常發生時,有可能是因爲頁面保存在 swap
區或者磁盤文件上造成的,所以我們需要利用頁面替換算法解決這個問題。
頁面替換主要分爲兩個方面,頁面換入和頁面換出。
- 頁面換入:主要在上述的
do_pgfault()
函數實現; - 頁面換出:主要在
swap_out_vistim()
函數實現。
在換入時,需要先檢查產生訪問異常的地址是否屬於某個 VMA
表示的合法虛擬地址,並且保存在硬盤的 swap
文件中(即對應的 PTE
的高 24
位不爲 0
,而最低位爲 0
),則是執行頁換入的時機,將調用 swap_in
函數完成頁面換入。
在換出時,採取的是消極的換出策略,是在調用 alloc_pages
函數獲取空閒頁時,此函數如果發現無法從物理內存頁分配器(比如 First Fit
)獲得空閒頁,就會進一步調用 swap_out
函數 換出某頁,實現一種消極的換出策略。
由於頁面換入操作已經在練習 1 中實現了,所以這裏我們主要談頁面換出。
爲了實現各種頁替換算法,我們需要設計一個頁替換算法的類框架 swap_manager
:
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);
};
這裏關鍵的兩個函數指針是 map_swappable
和 swap_out_vistim
,map_swappable
函數用於記錄頁訪問情況相關屬性,swap_out_vistim
函數用於挑選需要換出的頁。顯然 swap_out_vistim
函數依賴於 map_swappable
函數記錄的頁訪問情況。tick_event
函數指針也很重要,結合定時產生的中斷,可以實現一種積極的換頁策略。
FIFO
替換算法會維護一個隊列,隊列按照頁面調用的次序排列,越早被加載到內存的頁面會越早被換出。
由於 FIFO 基於雙向鏈表實現,所以只需要將元素插入到頭節點之前。
實現過程如下:
/*
* (3)_fifo_map_swappable: According FIFO PRA, we should link the most recent arrival page at the back of pra_list_head qeueue
*/
// 作用: 將最近被用到的頁面添加到算法所維護的次序隊列。
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;
}
將雙向鏈表中頭部節點後面的第一個節點刪除,返回對應的頁地址(虛擬地址)。
實現過程如下:
/*
* (4)_fifo_swap_out_victim: According FIFO PRA, we should unlink the earliest arrival page in front of pra_list_head qeueue,
* then assign the value of *ptr_page to the addr of this 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 qeueue
//(2) assign the value of *ptr_page to the addr of this page
/* Select the tail */
list_entry_t *le = head->prev;//指出需要被換出的頁
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;
}
最終運行結果如下:
如果要在ucore上實現"extended clock 頁替換算法"請給你的設計方案,現有的 swap_manager 框架是否足以支持在 ucore 中實現此算法?如果是,請給你的設計方案。如果不是,請給出你的新的擴展和基此擴展的設計方案。並需要回答如下問題
- 需要被換出的頁的特徵是什麼?
- 在ucore中如何判斷具有這樣特徵的頁?
- 何時進行換入和換出操作?
對於每個頁面都有兩個標誌位,分別爲使用位和修改位,記爲<使用,修改>
。換出頁的使用位必須爲0,並且算法優先考慮換出修改位爲零的頁面。
當內存頁被訪問後,MMU 將在對應的頁表項的 PTE_A
這一位設爲1;
當內存頁被修改後,MMU 將在對應的頁表項的 PTE_D
這一位設爲1。
當保存在磁盤中的內存需要被訪問時,需要進行換入操作;
當位於物理頁框中的內存被頁面替換算法選擇時,需要進行換出操作。
擴展練習
Challenge 1:實現識別 dirty bit 的 extended clock 頁替換算法(需要編程)
數據結構
Enhanced Clock 算法需要一個環形鏈表和一個指針,這個可以在原有的雙向鏈表基礎上實現。爲了方便進行循環訪問,將原先的頭部哨兵刪除,這樣所有的頁面形成一個環形鏈表。指向環形鏈表指針也就是 Enhanced Clock 算法中指向下個頁面的指針。
插入
如果環形鏈表爲空,那麼這個頁面就是整個鏈表,將指針指向這個頁面。否則,只需要將頁面插入指針指向的頁面之前即可。
換出
Enhanced Clock 算法最多需要遍歷環形鏈表四次(規定標記爲<訪問,修改>
):
- 首先,查找標記爲
<0,0>
的頁面; - 如果上一步沒有找到,查找標記
<0,1>
,並將訪問過的頁面的訪問位清零; - 如果上一步沒有找到,再次查找標記爲
<0,0>
的頁面; - 如果上一步沒有找到,再次查找標記爲
<0,1>
的頁面;
將PTE中的PTE_A清除後,需要調用
tlb_invalidate
刷新TLB,否則當頁面被再次訪問的時候,PTE中的PTE_A不會被設置。
實現如下:
// swap_clock.h
#ifndef __KERN_MM_SWAP_CLOCK_H__
#define __KERN_MM_SWAP_CLOCK_H__
#include <swap.h>
extern struct swap_manager swap_manager_clock;
#endif
--------------------------------------------------------------------------------------------
// swap_clock.c
#include <x86.h>
#include <stdio.h>
#include <string.h>
#include <swap.h>
#include <swap_clock.h>
#include <list.h>
#define GET_LIST_ENTRY_PTE(pgdir, le) (get_pte((pgdir), le2page((le), pra_page_link)->pra_vaddr, 0))
#define GET_DIRTY_FLAG(pgdir, le) (*GET_LIST_ENTRY_PTE((pgdir), (le)) & PTE_D)
#define GET_ACCESSED_FLAG(pgdir, le) (*GET_LIST_ENTRY_PTE((pgdir), (le)) & PTE_A)
#define CLEAR_ACCESSED_FLAG(pgdir, le) do {\
struct Page *page = le2page((le), pra_page_link);\
pte_t *ptep = get_pte((pgdir), page->pra_vaddr, 0);\
*ptep = *ptep & ~PTE_A;\
tlb_invalidate((pgdir), page->pra_vaddr);\
} while (0)
static int
_clock_init_mm(struct mm_struct *mm)
{
mm->sm_priv = NULL;
return 0;
}
static int
_clock_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);
// Insert before pointer
if (head == NULL) {
list_init(entry);
mm->sm_priv = entry;
} else {
list_add_before(head, entry);
}
return 0;
}
static int
_clock_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 *selected = NULL, *p = head;
// Search <0,0>
do {
if (GET_ACCESSED_FLAG(mm->pgdir, p) == 0 && GET_DIRTY_FLAG(mm->pgdir, p) == 0) {
selected = p;
break;
}
p = list_next(p);
} while (p != head);
// Search <0,1> and set 'accessed' to 0
if (selected == NULL)
do {
if (GET_ACCESSED_FLAG(mm->pgdir, p) == 0 && GET_DIRTY_FLAG(mm->pgdir, p)) {
selected = p;
break;
}
CLEAR_ACCESSED_FLAG(mm->pgdir, p);
p = list_next(p);
} while (p != head);
// Search <0,0> again
if (selected == NULL)
do {
if (GET_ACCESSED_FLAG(mm->pgdir, p) == 0 && GET_DIRTY_FLAG(mm->pgdir, p) == 0) {
selected = p;
break;
}
p = list_next(p);
} while (p != head);
// Search <0,1> again
if (selected == NULL)
do {
if (GET_ACCESSED_FLAG(mm->pgdir, p) == 0 && GET_DIRTY_FLAG(mm->pgdir, p)) {
selected = p;
break;
}
p = list_next(p);
} while (p != head);
// Remove pointed element
head = selected;
if (list_empty(head)) {
mm->sm_priv = NULL;
} else {
mm->sm_priv = list_next(head);
list_del(head);
}
*ptr_page = le2page(head, pra_page_link);
return 0;
}
static int
_clock_check_swap(void) {
cprintf("write Virt Page c in fifo_check_swap\n");
*(unsigned char *)0x3000 = 0x0c;
assert(pgfault_num==4);
cprintf("write Virt Page a in fifo_check_swap\n");
*(unsigned char *)0x1000 = 0x0a;
assert(pgfault_num==4);
cprintf("write Virt Page d in fifo_check_swap\n");
*(unsigned char *)0x4000 = 0x0d;
assert(pgfault_num==4);
cprintf("write Virt Page b in fifo_check_swap\n");
*(unsigned char *)0x2000 = 0x0b;
assert(pgfault_num==4);
cprintf("write Virt Page e in fifo_check_swap\n");
*(unsigned char *)0x5000 = 0x0e;
assert(pgfault_num==5);
cprintf("write Virt Page b in fifo_check_swap\n");
*(unsigned char *)0x2000 = 0x0b;
assert(pgfault_num==5);
cprintf("write Virt Page a in fifo_check_swap\n");
*(unsigned char *)0x1000 = 0x0a;
assert(pgfault_num==6);
cprintf("write Virt Page b in fifo_check_swap\n");
*(unsigned char *)0x2000 = 0x0b;
assert(pgfault_num==6);
cprintf("write Virt Page c in fifo_check_swap\n");
*(unsigned char *)0x3000 = 0x0c;
assert(pgfault_num==7);
cprintf("write Virt Page d in fifo_check_swap\n");
*(unsigned char *)0x4000 = 0x0d;
assert(pgfault_num==8);
cprintf("write Virt Page e in fifo_check_swap\n");
*(unsigned char *)0x5000 = 0x0e;
assert(pgfault_num==9);
cprintf("write Virt Page a in fifo_check_swap\n");
assert(*(unsigned char *)0x1000 == 0x0a);
*(unsigned char *)0x1000 = 0x0a;
assert(pgfault_num==9);
cprintf("read Virt Page b in fifo_check_swap\n");
assert(*(unsigned char *)0x2000 == 0x0b);
assert(pgfault_num==10);
cprintf("read Virt Page c in fifo_check_swap\n");
assert(*(unsigned char *)0x3000 == 0x0c);
assert(pgfault_num==11);
cprintf("read Virt Page a in fifo_check_swap\n");
assert(*(unsigned char *)0x1000 == 0x0a);
assert(pgfault_num==12);
cprintf("read Virt Page d in fifo_check_swap\n");
assert(*(unsigned char *)0x4000 == 0x0d);
assert(pgfault_num==13);
cprintf("read Virt Page b in fifo_check_swap\n");
*(unsigned char *)0x1000 = 0x0a;
assert(*(unsigned char *)0x3000 == 0x0c);
assert(*(unsigned char *)0x4000 == 0x0d);
assert(*(unsigned char *)0x5000 == 0x0e);
assert(*(unsigned char *)0x2000 == 0x0b);
assert(pgfault_num==14);
return 0;
}
static int
_clock_init(void)
{
return 0;
}
static int
_clock_set_unswappable(struct mm_struct *mm, uintptr_t addr)
{
return 0;
}
_clock_tick_event(struct mm_struct *mm)
{ return 0; }
struct swap_manager swap_manager_clock =
{
.name = "clock swap manager",
.init = &_clock_init,
.init_mm = &_clock_init_mm,
.tick_event = &_clock_tick_event,
.map_swappable = &_clock_map_swappable,
.set_unswappable = &_clock_set_unswappable,
.swap_out_victim = &_clock_swap_out_victim,
.check_swap = &_clock_check_swap,
};
Challenge 2:實現不考慮實現開銷和效率的 LRU 頁替換算法(需要編程)
注:待完成