structPage { // 引用计数 int ref; // page frame's reference counter // 用两个bit表示两种属性 // 1. PG_reserved位 此位为1时表示该页是内核保留的,不可分配 // 2. PG_property位 此位为1时表示可分配,为0表示已分配 uint32_t flags; // array of flags that describe the status of the page frame // 空闲块大小(以物理页位大小),仅在当前页是空闲块的第一个物理页时有效 unsignedint property; // the num of free block, used in first fit pm manager list_entry_t page_link; // free list link
/* 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. */ intdo_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr) { int ret = -E_INVAL; // try to find a vma which include addr // 尝试找到一个包含addr的vma // 也就是看一下这个地址是不是在进程某个vma的范围内 // vma管理的是所有有效地址,所以如果不在vma中,就是不合法的 structvma_struct *vma = find_vma(mm, addr);
pgfault_num++; // If the addr is in the range of a mm's vma? // 没有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: // 进到此处,说明对页面是写请求,同时页面存在于物理内存中 // 但是还没有检查权限,权限的检查是case 2中的代码 // 如果权限也满足,就可以直接写入了,不需要置换 /* error code flag : default is 3 ( W/R=1, P=1): write, present */ case2: /* error code flag : (W/R=1, P=0): write, not present */ // 写页面,但是页面不存在 // 检查下权限,没有写入权限就fail 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; case1: /* error code flag : (W/R=0, P=1): read, present */ // 读内存,且内存也存在于物理内存中 // 肯定是可以直接读的,不应该进入缺页中断中,直接错误 cprintf("do_pgfault failed: error code flag = read AND present\n"); goto failed; case0: /* error code flag : (W/R=0, P=0): read, not present */ // 读内存,但是内存不存在于物理内存中 // 检查下权限,没有读权限就fail 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 */ // 检查完权限,下面正式处理
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 * */ #if 1
/*LAB3 EXERCISE 1: YOUR CODE*/ // 先尝试获取pte,如果不存在,get_pte会自动创建一个pte表项 ptep = get_pte(mm->pgdir, addr, 1); //(1) try to find a pte, if pte's PT(Page Table) isn't existed, then create a PT. if (ptep == NULL) { goto failed; } // 如果这个表项对应的物理地址不存在,那么就需要分配一个物理页 if (*ptep == 0) { // 分配一个物理页,并让虚拟地址addr映射到这个物理页 // 就是让之前分配的pte表项指向这个物理页 if (pgdir_alloc_page(mm->pgdir, addr, perm) == NULL) goto failed; //(2) if the phy addr isn't exist, then alloc a page & map the phy addr with logical addr } else // 如果PTE存在,说明该页被置换到外存,需要换回来 { /*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; // 先把目标地址加载到内存page页上 ret = swap_in(mm, addr, &page); if (ret != 0) { goto failed; }
// 然后把page页映射到虚拟地址addr上,权限为perm ret = page_insert(mm->pgdir, page, addr, perm);
// 然后设置一下page页的虚拟地址 page->pra_vaddr = addr; //(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; }
assert(entry != NULL && head != NULL); // record the page access situlation /*LAB3 EXERCISE 2: YOUR CODE*/ // 就是将这一页加入到链表头中(最近访问过的放前面) 使其可以被置换算法使用到 list_add(head, entry); //(1)link the most recent arrival page at the back of the pra_list_head qeueue. return0; } /* * (4)_fifo_swap_out_victim: According FIFO PRA, we should unlink the earliest arrival page in front of pra_list_head qeueue, * then set the addr of addr of this page to ptr_page. */ // 选择页面置换出去 staticint _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*/ // 选择最早的页面作为置换出去的页面 list_entry_t *victim = head->prev; // 查找到该页面的链表项 *ptr_page = le2page(victim, pra_page_link); // 将该页面从链表中删除 list_del(victim); //(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 return0; }