关闭

ucore lab3实验报告

781人阅读 评论(0) 收藏 举报
分类:

Lab3实验报告

借助于页表机制和实验一涉及的中断异常处理机制,完成PageFault异常处理和FIFO算法的实现,结合磁盘提供的缓存空间,从而能够支持虚存管理。


练习0:填写以有实验

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

练习1:给未被映射的地址映上物理页

设置访问权限的时候需要参考页面所在的VMA的权限,映射物理页时需要操作内存控制结构所指定的页表,不是内核的页表。
启动分页机制后,如果一条指令或数据的虚拟地址所对应的物理页不在内存中,或者访问权限不够,就会产生页错误异常。


1.页表全为0–虚拟地址与物理地址未建立映射关系或者关系撤销
2.物理页不在内存中,需要进行换页机制
3.访问权限不够,报错

  • do_pgfault(mm/vmm.c)

//try to find a vma which include addr
// mm->mmap_cache = NULL;
//     mm->pgdir = NULL;
 //   mm->map_count = 0;

   // if (swap_init_ok) swap_init_mm(mm);
   // else mm->sm_priv = NULL;
struct vma_struct *vma = find_vma(mm, addr);

//pagefault_num的初始值为0
pgfault_num++;

/*
the virtual continuous memory area(vma)
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
uint32_t vm_flags;       // flags of vma
list_entry_t list_link;  // linear list link which sorted by start addr of vma
};

    */
//尝试获得页表入口
if ((ptep = get_pte(mm->pgdir, addr, 1)) == NULL) {
    cprintf("get_pte in do_pgfault failed\n");
    goto failed;
}
//页表不存在,页表项全为0
if (*ptep == 0) { // if the phy addr isn't exist, then alloc a page & map the phy addr with logical addr
    //perm设置权限
    //尝试申请一个页,如果申请失败,就是内存不足了,退出
    if (pgdir_alloc_page(mm->pgdir, addr, perm) == NULL) {
        cprintf("pgdir_alloc_page in do_pgfault failed\n");
        goto failed;
    }
}
//页表项非空,尝试换入页面
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中,此时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);
    }
    else {
        cprintf("no swap_init_ok but ptep is %x, failed\n",*ptep);
        goto failed;
    }
}
ret = 0;
failed:
return ret;

问题回答

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

  • 如果ucore的缺页服务例程在执行过程中访问内存,出现了页访问异常,请问硬件要做哪些事情?
    CPU会把产生异常的线性地址存储在CR2中,并且把表示页访问异常类型的值(简称页访问异常错误码,errorCode)保存在中断栈中。

[提示]页访问异常错误码有32位。位0为1表示对应物理页不存在;位1为1表示写异常(比如写了只读页;位2为1表示访问权限异常(比如用户态程序访问内核空间的数据)

[提示] CR2是页故障线性地址寄存器,保存最后一次出现页故障的全32位线性地址。CR2用于发生页异常时报告出错信息。当发生页异常时,处理器把引起页异常的线性地址保存在CR2中。操作系统中对应的中断服务例程可以检查CR2的内容,从而查出线性地址空间中的哪个页引起本次异常。
产生页访问异常后,CPU硬件和软件都会做一些事情来应对此事。首先页访问异常也是一种异常,所以针对一般异常的硬件处理操作是必须要做的,即CPU在当前内核栈保存当前被打断的程序现场,即依次压入当前被打断程序使用的EFLAGS,CS,EIP,errorCode;由于页访问异常的中断号是0xE,CPU把异常中断号0xE对应的中断服务例程的地址(vectors.S中的标号vector14处)加载到CS和EIP寄存器中,开始执行中断服务例程。这时ucore开始处理异常中断,首先需要保存硬件没有保存的寄存器。在vectors.S中的标号vector14处先把中断号压入内核栈,然后再在trapentry.S中的标号__alltraps处把DS、ES和其他通用寄存器都压栈。自此,被打断的程序执行现场(context)被保存在内核栈中。接下来,在trap.c的trap函数开始了中断服务例程的处理流程,大致调用关系为:

trap–> trap_dispatch–>pgfault_handler–>do_pgfault
产生页访问异常后,CPU把引起页访问异常的线性地址装到寄存器CR2中,并给出了出错码errorCode,说明了页访问异常的类型。ucore OS会把这个值保存在struct trapframe 中tf_err成员变量中。而中断服务例程会调用页访问异常处理函数do_pgfault进行具体处理。这里的页访问异常处理是实现按需分页、页换入换出机制的关键之处。

ucore中do_pgfault函数是完成页访问异常处理的主要函数,它根据从CPU的控制寄存器CR2中获取的页访问异常的物理地址以及根据errorCode的错误类型来查找此地址是否在某个VMA的地址范围内以及是否满足正确的读写权限,如果在此范围内并且权限也正确,这认为这是一次合法访问,但没有建立虚实对应关系。所以需要分配一个空闲的内存页,并修改页表完成虚地址到物理地址的映射,刷新TLB,然后调用iret中断,返回到产生页访问异常的指令处重新执行此指令。如果该虚地址不在某VMA范围内,则认为是一次非法访问。

练习2:补充完成基于FIFO的页面替换算法

vmm.c的do_pgfault

   if(swap_init_ok) {
        struct Page *page=NULL;
        //根据mm结构和addr地址,尝试将硬盘中的内容换入page中,此时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);
    }
    else {
        cprintf("no swap_init_ok but ptep is %x, failed\n",*ptep);
        goto failed;
    } 

swap _fifo.c

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: 2013011424*/ 
//(1)link the most recent arrival page at the back of the pra_list_head qeueue.
//将最近使用的页面添加到次序的队尾
list_add(head, entry);
return 0;
 }

_ fifo _ swap_out _victim

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: 2013011424*/ 
 //(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 */
//用le指示需要被换出的页
 list_entry_t *le = head->prev;
 assert(head!=le);
//le2page 宏可以根据链表元素,获得对应page的指针
 struct Page *p = le2page(le, pra_page_link);
//将最早进来的页面从队列中删除
 list_del(le);
 assert(p !=NULL);
//将这一页的地址存储在ptr_page中
 *ptr_page = p;
 return 0;
}

问题回答

  • 如果要在ucore上实现”extended clock页替换算法”请给你的设计方案,现有的swap_manager框架是否足以支持在ucore中实现此算法?如果是,请给你的设计方案。如果不是,请给出你的新的扩展和基此扩展的设计方案。并需要回答如下问题
    需要被换出的页的特征是什么?
    在ucore中如何判断具有这样特征的页?
    何时进行换入和换出操作?

实验运行截图

lab3实验运行截图

扩展练习 Challenge

  • 现识别dirty bit的 extended clock页替换算法
    根据页面近期是否被修改从而决定页面是否被换出,在查询空闲页时加上对dirty bit的判断。

操作系统淘汰页面时,对当前指针指向的页所对应的页表项进行查询,如果dirty bit为0,把页换到硬盘上,如果dirty bit 为1,则将dirty bit改为0,继续访问下一页

编写_extend_clock()然后更改默认替换算法.

mm/swap_fifo.c

struct swap_manager swap_manager_fifo =
{
 .name            = "fifo swap manager",
 .init            = &_fifo_init,
 .init_mm         = &_fifo_init_mm,
 .tick_event      = &_fifo_tick_event,
 .map_swappable   = &_fifo_map_swappable,
 .set_unswappable = &_fifo_set_unswappable,
// .swap_out_victim = &_fifo_swap_out_victim,
//更改默认的算法
.swap_out_victim = &_extended_clock_swap_out_victim,
 .check_swap      = &_fifo_check_swap,
};

在mm/swap_fifo.c添加函数

static int _extended_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*le=head->prev;
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<0x1000)
    {
        break;
    }
    if(!(*ptep&PTE_D))
    {
        //如果dirty bit 为0,换出页面,将页面从队列中删除
        list_del(le);
        assert(p!=NULL);
        //将这一个页放在ptr_page中
        *ptr_page=p;
        return 0;
    }else{
        //如果为1 ,赋值为0跳过
        *ptep&=0xffffffbf;
    }
    le=le->prev;
}
le=head->prev;
assert(head!=le);
struct Page*p=le2page(le,pra_page_link);

list_del(le);
assert(p!=NULL);

 *ptr_page=p;
return 0;

}

1
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:67942次
    • 积分:1956
    • 等级:
    • 排名:千里之外
    • 原创:128篇
    • 转载:16篇
    • 译文:0篇
    • 评论:27条
    每个人都是过客,每个人都有故事
    也许深夜往往是人们内心最为脆弱的时刻。孤独,绝望,失意,无奈......这些复杂沉重的情绪会随着黑夜的来临不再躲藏。
    最新评论