练习一:给未被映射的地址映射上物理页
1、描述:
在lab2中我们实现并启动了分页机制,现在我们需要考虑什么情况下会产生页错误异常并触发中断。显答案是在指令或数据的虚拟地址所对应的物理页不在内存中,或者访问权限不够的情况下。其具体原因有以下三点:
①页表项全为0——虚拟地址与物理地址未建立映射关系或已被撤销。
②物理页面不在内存中——需要进行换页机制。
③访问权限不够——输出错误信息,并退出。
根据以上三点错误原因,完成页错误处理函数do_pgfault()。
2、思路:
do_pgfault()函数从CR2寄存器中获取页错误异常的虚拟地址,根据error code来查找这个虚拟地址是否在某一个VMA的地址范围内,并且具有正确的权限。如果满足上述两个要求,则需要为分配一个物理页。
3、实现:
//页表项非空,可以尝试换入页面
else{
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;
}
}
练习二:补充完成基于FIFO()的页面替换算法。
1、描述:
在上面的练习中,页错误异常发生时,有可能是因为页面保存在swap区或者磁盘文件上造成的,练习二需要利用页面替换算法解决这个问题。
2、思路:
页面替换主要分为两个方面,页面换出和页面换入。页面换入主要在vmm.c中的do_pgfault()函数实现;页面换出主要在swap_fifo.c中的swap_out_vistim()函数实现。 在换入时,需要先检查产生访问异常的地址是否属于某个vma表示的合法虚 拟地址,并且保存在硬盘的swap文件中(对应的PTE的高24位不为0)。如果满足以上亮点,则执行swap_in() 函数换入页面。
换出则相对简单,当申请空闲页面时,alloc_pages()函数不能获得空闲页,则直接调用swap_out()函数换出不常用的页面。
3、实现:
(一)、页面换入的实现
//页表项非空,可以尝试换入页面
else{
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;
}
}
(二)、页面换出的关键代码
FIFO替换算法会维护一个队列,队列按照页面调用的次序排列,越早被加载到内存的页面会越早被换出。下面几个关键函数在swap_out()中被调用。
①_fifo_map_swappable()函数将最近被用到的页面添加到算法所维护的次序队列
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);
//将最近用到的页面添加到次序队尾
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);
//用le指示需要被换出的页
list_entry_t *le = head->prev;
assert(head!=le);
//le2page宏可以根据链表元素获得对应的Page指针p
struct Page *p = le2page(le, pra_page_link);
//将进来最早的页面从队列中删除
list_del(le);
assert(p !=NULL);
//将这一页的地址存储在ptr_page中
*ptr_page = p;
return 0;
}
结果部分截图: