主要姓名:馮成 學號:19021221183 學院:電子工程學院
lab3 延續(xù)1和2橄杨。實現(xiàn)了配套的虛擬機制账嚎。主要是包括缺頁異常處理今缚。
相較于lab2已有代碼的改變:
struct Page *
alloc_pages(size_t n) {
struct Page *page=NULL;
bool intr_flag;
while (1)
{
local_intr_save(intr_flag);
{
page = pmm_manager->alloc_pages(n);
}
local_intr_restore(intr_flag);
// 為什么N不能大于1
if (page != NULL || n > 1 || swap_init_ok == 0) break;
extern struct mm_struct *check_mm_struct;
//cprintf("page %x, call swap_out in alloc_pages %d\n",page, n);
// 在分配頁時察皇,如果沒有可用的物理內(nèi)存(在空閑鏈表上查找)蕉堰,就將一部分內(nèi)存換出到磁盤若锁。
swap_out(check_mm_struct, n, 0);
}
//cprintf("n %d,get page %x, No %d in alloc_pages\n",n,page,(page-pages));
return page;
}
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) 虛擬地址的鏈表搁骑,每次缺頁時,會加載一個頁又固,并將該頁放到這個中仲器。根據(jù)這個鏈表前后順序可以達到需要換出的物理頁面。
uintptr_t pra_vaddr; // used for pra (page replace algorithm) 虛擬地址
};
//
同時在trap.c中加入了缺頁異常的處理程序仰冠。init.c 中也加入了對磁盤的初始化乏冀。本實驗中主要是為了理解操作系統(tǒng)的實現(xiàn)。所以我們將對磁盤的操作看成一個api即可洋只。以
swapfs_write為例辆沦。
swapfs_write(swap_entry_t entry, struct Page *page) 表示將page空間的內(nèi)存寫入到entry位置的磁盤中昼捍。為了實現(xiàn)這個lab,在理解完整個流程后就要明確check_swap函數(shù)众辨。
首先了解有關虛擬內(nèi)存的數(shù)據(jù)結構
// mm_struct和vma_struct都是現(xiàn)代linux內(nèi)核中重要的東西端三。
// 首先每個程序只有一個mm_struct結構體,表示這個進程的內(nèi)存管理器鹃彻。
// 這個進程又有多個內(nèi)存區(qū)域郊闯,對應于只讀區(qū),靜態(tài)區(qū)蛛株,代碼區(qū)等等(這些區(qū)域分別對應一個vma_struct)团赁。
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
};
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
};
檢測程序.
// 引用了部分代碼
static void check_swap(void)
{
pde_t *pgdir = mm->pgdir = boot_pgdir;
assert(pgdir[0] == 0);
// 0x1000-0x6000, 這里實際上就是0x1000-0x5999可用。一共5頁谨履。(虛擬頁)
struct vma_struct *vma = vma_create(BEING_CHECK_VALID_VADDR, CHECK_VALID_VADDR, VM_WRITE | VM_READ);
assert(vma != NULL);
insert_vma_struct(mm, vma);
for (i = 0; i < CHECK_VALID_PHY_PAGE_NUM; i++)
{
check_rp[i] = alloc_page();
}
list_entry_t free_list_store = free_list;
list_init(&free_list);
assert(list_empty(&free_list));
// 此時空閑鏈表中沒有數(shù)據(jù)欢摄,再通過下面釋放四頁∷袼冢總共有四頁可用
unsigned int nr_free_store = nr_free;
nr_free = 0;
for (i = 0; i < CHECK_VALID_PHY_PAGE_NUM; i++)
{
free_pages(check_rp[i], 1);
}
// 中間將空閑鏈表清空后怀挠,然后釋放4頁。
assert(nr_free == CHECK_VALID_PHY_PAGE_NUM);
// 總結:現(xiàn)在就是一共4個物理空閑頁害捕,虛擬空間一共5頁绿淋。
// 那么一定的,對其中一個頁的訪問需要借助磁盤了尝盼。
pgfault_num = 0;
check_content_set();
// 此時0x1000-0x4999一共四個頁都分配了物理地址
ret = check_content_access();
}
static inline void
check_content_set(void)
{
// 這里的缺頁函數(shù)都會進入到do_pgfault的pgdir_alloc_page中吞滞,而且此時是不需要換出的。
// 在執(zhí)行這前盾沫,這些內(nèi)存值都沒有被訪問裁赠,所以每第一次訪問會出現(xiàn)異常。
// 第二次可以正常訪問赴精,所以缺頁總數(shù)第一次訪問時都是加1的佩捞。第二次是相等的
*(unsigned char *)0x1000 = 0x0a;
assert(pgfault_num == 1);
*(unsigned char *)0x1010 = 0x0a;
assert(pgfault_num == 1);
*(unsigned char *)0x2000 = 0x0b;
assert(pgfault_num == 2);
*(unsigned char *)0x2010 = 0x0b;
assert(pgfault_num == 2);
*(unsigned char *)0x3000 = 0x0c;
assert(pgfault_num == 3);
*(unsigned char *)0x3010 = 0x0c;
assert(pgfault_num == 3);
*(unsigned char *)0x4000 = 0x0d;
assert(pgfault_num == 4);
*(unsigned char *)0x4010 = 0x0d;
assert(pgfault_num == 4);
}
// 但是當執(zhí)行
static int
_fifo_check_swap(void) {
// 0x1000-0x4999一共四個頁都分配了物理地址
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);
// 這里一定會出現(xiàn)缺頁。在check_swap中將空閑物理頁設置為4個了蕾哟,當訪問0x5000時失尖,就需要加載新的頁,但是此時的空閑物理頁已經(jīng)用完渐苏,所以需要換出掀潮。
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==7);
cprintf("write Virt Page c in fifo_check_swap\n");
*(unsigned char *)0x3000 = 0x0c;
assert(pgfault_num==8);
cprintf("write Virt Page d in fifo_check_swap\n");
*(unsigned char *)0x4000 = 0x0d;
assert(pgfault_num==9);
cprintf("write Virt Page e in fifo_check_swap\n");
*(unsigned char *)0x5000 = 0x0e;
assert(pgfault_num==10);
cprintf("write Virt Page a in fifo_check_swap\n");
assert(*(unsigned char *)0x1000 == 0x0a);
*(unsigned char *)0x1000 = 0x0a;
assert(pgfault_num==11);
return 0;
}
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
// 當發(fā)生缺頁異常時,硬件將會對error_code壓棧琼富,并且將出錯的地址寫入cr2寄存器中仪吧。
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;
}
}
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;
#if 1
ptep = get_pte(mm->pgdir, addr, 1);
struct Page *page;
/*LAB3 EXERCISE 1: YOUR CODE*/
//(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
page = pgdir_alloc_page(mm->pgdir, addr, perm);
}
else
{ // 執(zhí)行到這里只有一種情況就是,pte表示的是磁盤中的位置鞠眉。不可能是正常的物理地址薯鼠,因為正常的物理地址不會走異常中斷的
cprintf("****** need swap\n");
if (swap_init_ok)
{
struct Page *page = NULL;
swap_in(mm, addr, &page);
page->pra_vaddr =addr;
page_insert(mm->pgdir, page, addr, perm);
swap_map_swappable(mm, addr, page, 1);
//(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
ret = 0;
failed:
return ret;
}
訪問一個內(nèi)存時择诈,如果異常,卻地址范圍和權限都是正確的出皇,就會進入異常處理程序羞芍。如果這個地址對應的頁沒有映射,就給他分配一個頁郊艘。在分配一個頁的過程中荷科,如果沒有可用物理地址就將最早缺頁異常的空間換出到磁盤空間。此時這個地址對應的pte是磁盤中的位置纱注。當下一次訪問這個地址的時候一定出現(xiàn)缺頁異常畏浆。只是這個地址對應的pte是有值的,這個值是可磁盤中的位置是有關聯(lián)的狞贱,所以可以將磁盤中的數(shù)據(jù)加載到這個虛擬內(nèi)存(缺頁異常地址)對應的頁中刻获。