操作系统实验三实验报告

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jasonyuchen/article/details/76561107

实验三:虚拟内存管理


练习0:填写已有实验

参考实验二中的做法,使用meld可以很快完成

http://blog.csdn.net/jasonyuchen/article/details/76424857


练习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
阅读更多
换一批

没有更多推荐了,返回首页