中科大信息安全操作系统课程ucore lab3实验报告

操作系统lab3实验报告

PB15051157 茹思淞

实验三

本次实验是在实验二的基础上, 借助于页表机制和实验一中涉及的中断异常处理机制, 完成Page Fault异常处理和FIFO页替换算法的实现, 结合磁盘提供的缓存空间, 从而能够支持虚存管理, 提供一个比实际物理内存空间“更大”的虚拟内存空间给系统使用。 这个实验与实际操作系统中的实现比较起来要简单, 不过需要了解实验一和实验二的具体实现。 实际操作系统系统中的虚拟内存管理设计与实现是相当复杂的, 涉及到与进程管理系统、 文件系统等的交叉访问。 如果大家有余力, 可以尝试完成扩展练习, 实现extended clock页替换算法。

练习0:填写已有实验

本实验依赖实验1/2。 请把你做的实验1/2的代码填入本实验中代码中有“LAB1”,“LAB2”的注释相应部分。

下面是实验指导书给出的参考,仔细阅读这些改动。

中科大信息安全操作系统课程ucore lab3实验报告_第1张图片

中科大信息安全操作系统课程ucore lab3实验报告_第2张图片

本练习比较简单,仅仅是将实验1、2的代码进行复制粘贴即可,因此不在此赘述。

练习1:给未被映射的地址映射上物理页( 需要编程)

完成do_pgfault( mm/vmm.c) 函数, 给未被映射的地址映射上物理页。 设置访问权限 的时候需要参考页面所在 VMA 的权限, 同时需要注意映射物理页时需要操作内存控制 结构所指定的页表, 而不是内核的页表。 注意:在LAB2 EXERCISE 1处填写代码。

下面是练习1所需要补全的代码,依赖注释我们可以轻松得到:

    /*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 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

    }
#end if

    //下面是补全的代码
    if ((ptep = get_pte(mm->pgdir, addr, 1)) == NULL) {//尝试找到pte,若对应的页表项不存在,则转去创建一个页表项
        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;
        }
    }

执行make qemu后得到实验结果基本没有问题,具体的流程参看中文注释:

(a)请描述页目录项( Pag Director Entry) 和页表( Page Table Entry) 中组成部分对ucore实现页替换算法的潜在用处。

页目录项是指向储存页表的页面的, 所以本质上与页表项相同, 结构也应该相同. 每个页表项的高20位, 就是该页表项指向的物理页面的首地址的高20位(当然物理页面首地址的低12位全为零), 而每个页表项的低12为, 则是一些功能位, 可以通过在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 用户可自定义的部分

对于实现页替换算法来说,页目录项(pgdir)作为一个双向链表存储了目前所有的页的物理地址和逻辑地址的对应,即在实内存中的所有页,替换算法中被换出的页从pgdir中选出。页表(pte)则存储了替换算法中被换入的页的信息,替换后会将其映射到一物理地址。

(b)如果ucore的缺页服务例程在执行过程中访问内存, 出现了页访问异常, 请问硬件要做哪些事情?

产生页访问异常后,CPU把引起页访问异常的线性地址装到寄存器CR2中,并给出了出错码errorCode,说明了页访问异常的类型。ucore OS会把这个值保存在struct trapframe 中tf_err成员变量中。而中断服务例程会调用页访问异常处理函数do_pgfault进行具体处理。

练习2:补充完成基于FIFO的页面替换算法( 需要编程)

完成vmm.c中的do_pgfault函数, 并且在实现FIFO算法的swap_fifo.c中完成map_swappable和swap_out_vistim函数。 通过对swap的测试。 注意:在LAB2 EXERCISE 2处填写代码。

下面是vmm.c文件中需要补全的do_pgfault部分,补完结果如下:

#if 0
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

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) {//pte是需要交换的表项
            struct Page *page=NULL;//创建一个新页
            if ((ret = swap_in(mm, addr, &page)) != 0) {//利用mm结构和addr地址,尝试将硬盘中的内容换入到新的page中
                cprintf("swap_in in do_pgfault failed\n");//若失败,则退出
                goto failed;
            }    
            page_insert(mm->pgdir, page, addr, perm);//将该页面插入到队列之中,并且建立虚拟地址与物理地址间的对应关系
            swap_map_swappable(mm, addr, page, 1);//设置该页面为可交换的
        }
        else {
            cprintf("no swap_init_ok but ptep is %x, failed\n",*ptep);
            goto failed;
        }
   }
   ret = 0;
failed:
    return ret;
}

下面是swap_fifo.c中需要补完的map_swappableswap_out_vistim函数,补完结果如下:

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);//将最近分配的页插入到pra_list_head队列的尾部
    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 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 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;//将该页的地址存储在ptr_page中
     return 0;
}

执行make qemu后得到实验结果无问题

(a)请在实验报告中简要说明你的设计实现过程

本实验的主要设计思路为:

  1. 若要完成基于FIFO规则的页面置换算法,首先,我们必须要完善虚拟内存管理函数,保证页面被换出之后,能够再次换入,在本次实验中do_pgfault即为进行页面换入。
  2. 其次则是解决FIFO页面置换算法中的两个问题,一是将最近被用到的页面添加到算法所维护的次序队列。二则是查询哪个页面需要被换出。这样即可以保证该算法的实现。
(b)如果要在ucore上实现”extended clock页替换算法”请给你的设计方案, 现有的swap_manager框架是否足以支持在ucore中实现此算法?如果是, 请给你的设计方案。 如果不是, 请给出你的新的扩展和基此扩展的设计方案。 并需要回答如下问题 :

我认为,目前的swap_manager框架足以支持在ucore中实现extended clock算法,在kern/mm/mmu.h文件中有如下定义:

/* page table/directory entry flags */
#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
                                                // The PTE_AVAIL bits aren't used by the kernel or interpreted by the
                                                // hardware, so user processes are allowed to set them arbitrarily.

#define PTE_USER        (PTE_U | PTE_W | PTE_P)

其中PTE_A中的内容的即标志着该页是否被访问过,由此我们可以实现extended clock算法。于是我们可以对kern/mm/swap_fifo.c做相应的修改,判断是否被访问过即可:

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);
     list_entry_t *le = head->next;
     assert(head != le);

     while(le != head) 
     {
         struct Page *p = le2page(le, pra_page_link);
         pte_t *ptep = get_pte(mm->pgdir, p->pra_vaddr, 0);      
         if(!(*ptep & PTE_A))
         {  //未被访问
             list_del(le);
             assert(p != NULL);
             *ptr_page = p;
             return 0;
         }
        *ptep ^= PTE_A;
        le = le->next;
     }
     le = le->next;
     while(le != head) 
     {
         struct Page *p = le2page(le, pra_page_link);
         pte_t *ptep = get_pte(mm->pgdir, p->pra_vaddr, 0); 
         list_del(le);
         assert(p != NULL);
         *ptr_page = p;
         return 0;
     }
}
  • 需要被换出的页的特征是什么?

    最早被换入,且最近没有被访问过的页。

  • 在ucore中如何判断具有这样特征的页?

    首先判断其最近有没有被访问过(利用条件*ptep & PTE_A进行判断),若无,则按照FIFO原则进行置换。

  • 何时进行换入和换出操作?

    当需要调用的页不在页表中时,并且在页表已满的情况下,需要进行换入和换出操作。

    扩展练习 Challenge:实现识别dirty bit的 extended clock页替换算法( 需要编程)

问题分析:算法根据页面近期是否被修改从而决定该页面是否应当被换出。所以在查询空闲页时,需要加上对dirty bit的判断。

大体思路:当操作系统需要淘汰页时,对当前指针指向的页所对应的页表项进行查询,如果dirty bit为0,则把此页换出到硬盘上;如果dirty bit为1,则将dirty bit置为0,继续访问下一个页。

相比较FIFO的操作,dirty bit的替换算法只需要识别出哪些页被访问过,以及哪些页被修改过即可。在kern/mm/mmu.h文件下有如下的定义:

#define PTE_A           0x020                   // Accessed
#define PTE_D           0x040                   // Dirty

其中PTE_A和PTE_D分别是表示访问和修改的标识位,因此与*ptep求与即可判断页是否被访问或修改过。首先根据基础的extended clock算法,未被访问的页应优先考虑换出;在此基础上,由于被修改的也需要被写回硬盘,因此未被修改的页应该有限换出。因此采用多轮循环。只需要修改kern/mm/vmm.h中的_fifo_swap_out_victim()函数即可实现:

_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);
    //将head指针指向最先进入的页面
     list_entry_t *le = head->next;
     assert(head != le);
     //查找最先进入并且未被修改的页面
     while(le != head) {
         struct Page *p = le2page(le, pra_page_link);
         //获取页表项
         pte_t *ptep = get_pte(mm->pgdir, p->pra_vaddr, 0); 
         //判断获得的页表项是否正确  
         if(!(*ptep & PTE_A) && !(*ptep & PTE_D)) { //未被访问,未被修改
             //如果dirty bit为0,换出
            //将页面从队列中删除
             list_del(le);
             assert(p != NULL);
            //将这一页的地址存储在prt_page中
             *ptr_page = p;
             return 0;
         }
         le = le->next;
     }
     le = le->next;
     while(le != head) {
         struct Page *p = le2page(le, pra_page_link);
         pte_t *ptep = get_pte(mm->pgdir, p->pra_vaddr, 0);      
         if(!(*ptep & PTE_A) && (*ptep & PTE_D)) {  //未被访问,已被修改
             list_del(le);
             assert(p != NULL);
             *ptr_page = p;
             return 0;
         }
         *ptep ^= PTE_A;    //页被访问过则将PTE_A位置0
         le = le->next;
     }
     le = le->next;
     while(le != head) {
         struct Page *p = le2page(le, pra_page_link);
         pte_t *ptep = get_pte(mm->pgdir, p->pra_vaddr, 0);      
         if(!(*ptep & PTE_D)) {     //未被修改,此时所有页均被访问过,即PTE_A位为0
             list_del(le);
             assert(p != NULL);
             *ptr_page = p;
             return 0;
         }
         le = le->next;
     }
     //如果这行到这里证明找完一圈,所有页面都不符合换出条件
    //那么强行换出最先进入的页面
     le = le->next;
     while(le != head) {
         struct Page *p = le2page(le, pra_page_link);
         pte_t *ptep = get_pte(mm->pgdir, p->pra_vaddr, 0);      
         if(*ptep & PTE_D) {    //已被修改
             list_del(le);
             assert(p != NULL);
            //将这一页的地址存储在ptr_page中
             *ptr_page = p;
             return 0;
         }
         le = le->next;
     }
}

你可能感兴趣的:(中科大信息安全操作系统课程ucore lab3实验报告)