操作系统——ucore Lab3
一、内容
本次实验是在实验二的基础上,借助于页表机制和实验一中涉及的中断异常处理机制,完Page Fault异常处理和FIFO页替换算法的实现,结合磁盘提供的缓存空间,从而能够支持虚存管理,提供一个比实际物理内存空间“更大”的虚拟内存空间给系统使用。实际操作系统系统中的虚拟内存管理设计与实现是相当复杂的,涉及到与进程管理系统、文件系统等的交叉访问。
二、目的
1、了解虚拟内存的Page Fault异常处理实现
2、了解页替换算法在操作系统中的实现
三、实验思想以及实验过程和结果分析
练习0:填写已有的实验
实验三依赖于实验已所写的内容,因此需要将实验一与实验二中的内容在实验二相对应的地方进行填写。填写完成后继续进行实验。
准备知识
**
- 虚拟内存:
**由于物理内存是由限制的,所以操作系统为了给CPU提供一个“无限大”的内存,抽线处理一个“虚拟内存”的概念,具体操作是将内存中不常用的数据转移到磁盘上,当需要使用时再将磁盘上的数据加载到内存中,这样内存看起来是“无限大”的,这时候内存相当于磁盘的一个Cache,也就存在了命中与不命中的机制,所以需要充分利用程序的空间与时间局部性
我们需要注意的是:
- 虚拟内存单元不一定有实际的物理内存单元对应,即实际的物理内存单元可能不存在;
- 如果虚拟内存单元对应有实际的物理内存单元,那二者的地址一般是不相等的;
- 通过操作系统实现的某种内存映射可建立虚拟内存与物理内存的对应关系,使得程序员 或CPU访问的虚拟内存地址会自动转换为一个物理内存地址
**
- Page Fault异常处理:
**在程序执行过程中由于某种原因使得CPU无法最终访问到相应的物理内存单元,即无法完成从虚拟地址到物理地址映射时,CPU 会产生 一次页访问异常,从而需要进行相应的页访问异常的中断服务例程,这个页访问异常处理的时机被操作系统充分利用来完成虚存管理,即实现“按需调页”/“页换入换出”处理的执行时机。 当相关处理完成后,页访问异常服务例程会返回到产生异常的指令处重新执行,使得应用软件可以继续正常运行下去。
产生页访问异常的原因主要有:
1、目标页帧不存在(页表项全为0,即该线性地址与物理地址尚未建立映射或者已经撤销);
2、相应的物理页帧不在内存中(页表项非空,但Present标志位=0,比如在swap分区或磁盘文件上),这在本次实验中会出现,我们将在下面介绍换页机制实现时进一步讲解如何处理;
3、不满足访问权限(此时页表项P标志=1,但低权限的程序试图访问高权限的地址空间,或者有程序试图写只读页面)
出现上述异常时,操作系统便会调用do_pgfault函数,进行相应的处理。其中一些错误是需要反馈给用户的(即不可修复的,如权限错误),而有一些是需要操作系统来进行相应的操作以便让程序顺利进行下去。
需要用到的数据结构:
vma_struct
// 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
};
用来表示连续的虚拟内存区
vm_mm:使用同一PDT(一级页表)的控制器入口
vm_start:连续内存的开始地址
vm_end:连续内存的结束地址
vm_flags:标志位
list_link:进行连接管理的链表
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
};
使用相同PDT的一组vma的控制结构
mmap_list:用于连接管理的链表
mmap_cache:当前可用空间
pgdir:PDT的入口地址
map_count:当前vma的数量
sm_priv:交换管理器的私有数据
-
练习1:给未被映射的地址映射上物理页
首先来查看一下函数原型:
参数:
mm:传入的vma控制器
error_code:错误的类型
addr:发生错误的地址,是线性地址
首先我们来看看函数一开始所做的分类工作,即将错误进行细分,如需反馈的直接反馈,否则往下处理。
1、一开始,通过find_vma函数进行查找,看管理器中是否存在传入的错误地址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;
}
2、接下来进行错误类型检查
//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;
}
}
第一种:申请写操作,物理内存中不存在,并且对应地址的内容不允许写
第二种:申请读操作,并且物理内存中存在(因此已经报错,所以是权限不够)
第三种:申请读操作但是物理内存中不存在,并且该地址数据不允许被读或者加载
排除这三种情况就是操作系统需要处理的情况
包括 写一个存在的地址 写一个不存在并且addr是可写的 读一个不存在地址并且addr是可读的
这时候我们就要进行相应的处理了,以便程序能够正常运行
1、首先去查找addr对应的PTD(二级页表)条目,如果不存在则分配一页,若分配出错,则报错
// try to find a pte, if pte's PT(Page Table) isn't existed, then create a PT.
// (notice the 3th parameter '1')
if ((ptep = get_pte(mm->pgdir, addr, 1)) == NULL) {
cprintf("get_pte in do_pgfault failed\n");
goto failed;
}
2、如果是新分配的PTD,那么相应的二级页表条目应该是空的(*ptep=0),这时候我们就分配一页物理空间,建立映射关系,如果分配失败,则报错
if (*ptep == 0) { // if the phy addr isn't exist, then alloc a page & map the phy addr with logical addr
if (pgdir_alloc_page(mm->pgdir, addr, perm) == NULL) {
cprintf("pgdir_alloc_page in do_pgfault failed\n");
goto failed;
}
}
- 练习2:补充完成基于FIFO的页面替换算法
剩下的就是信息被存放到磁盘上去的,这时候我们需要将这页信息从磁盘上交换回来
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;
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);
page->pra_vaddr = addr;
}
else {
cprintf("no swap_init_ok but ptep is %x, failed\n",*ptep);
goto failed;
}
}
1、首先确认交换初始化完毕 然后分配一页物理空间,利用swap_in函数,将磁盘上的信息交换到物理内存中
2、 然后利用page_insert函数将新申请的物理页面与线性地址建立映射关系
3、最后利用swap_map_swappable函数根据FIFO替换规则,将最近到达的页面链接到队列的最后
对上述出现的函数进行说明:
swap_in: 根据PTE中的addr交换项,得到对应在磁盘中的位置,读取相应位置的信息到内存页中
page_insert:将新申请的物理页面与线性地址建立映射关系
原型:_fifo_map_swappable
/mm/swap_fifo.c
swap_map_swappable:根据FIFO替换规则,将最近到达的页面链接到队列的最后
最后的一步工作便是将swap_fifo.c文件中的——fifo_swap_out_victim函数填充完整,这个函数的作用是在进行页替换时选择哪一页进行替换,由于我们采用的FIFO的方法,因此,只要取出队列的第一个进行替换就好了
/mm/swap_fifo.c
最后利用 make qemu进行结果查看,出现了
check_pgfault() succeeded!
check_swap() succeeded!
的字样,结果正确
实验完成