此实验借助页表机制和中断异常处理机制,完成page fault异常处理和fifo页替换算法的实现。ucore建立了mm_struct和vma_struct数据结构,描述了ucore模拟应用程序运行所需的合法内存空间。vma_struct描述的地址范围就是程序的合法虚拟地址范围。

struct vma_struct {struct mm_struct *vm_mm;//表示该vma隶属于的mm,每个mm中的所有vma均属于同一页目录表的虚拟内存空间uintptr_t vm_start; // 虚拟内存空间的起始位置uintptr_t vm_end; // 虚拟内存空间的结束位置uint32_t vm_flags; // 这块连续的虚拟内存空间的属性(只读、可读写、可执行)list_entry_t list_link;//该结构用于链表建立
};

每个进程都会有一个mm_struct用来管理虚拟内存和物理内存

struct mm_struct {list_entry_t mmap_list;// 双向链表头,链接了所有属于该mm的vmastruct vma_struct *mmap_cache;// 指向当前正在使用的虚拟内存空间。由于“局部性“原理,程序会经常使用正在用的虚拟内存空间,这属性将使得无需查询链表,查询将加速30%以上。pde_t *pgdir; // 指向vma所属于的页目录表int map_count; // 包含了vma的数量void *sm_priv; // 用于虚拟内存置换算法的属性,使用void*指针做到通用
};

涉及vma_struct的操作函数包括三个vma_create、insert_vma_struct、find_vma,分别是创建、插入链表、查找。

涉及mm_struct的操作函数仅有两个mm_create和mm_destory,功能同字面。

Page Fault异常处理

在程序的执行过程中,CPU无法访问到相应的物理内存,CPU将会产生一次该异常。

产生异常的原因主要有以下几点:

1、目标页帧不存在

2、对应的物理页帧并不在内存中

3、不满足访问权限

出现上述情况之一,产生page fault,CPU会把产生异常的线性地址存储在CR2中,并把表示页访问异常类型的值errorCode保存在中断栈中,具体流程可见lab1笔记。

ucore中的do_pgfault函数是完成页访问异常的主要函数,它根据CPU的CR2种获得页访问异常的地址以及根据errorCode的错误类型来查找此地址会否在某个VMA的地址范围以内以及是否满足正确的读写权限。

如果范围合法并且权限也正确,那么就分配一个空闲的内存页进行映射或者是将交换出去的内存页交换回来,刷新TLB,并调用iret中断,返回。

如果地址不在VMA的范围以内,或者是权限不足,那么就认定是一次非法访问。

页面置换机制

在操作系统的设计中,一个基本的原则是:并非所有的物理页可以交换出去,只有映射到用户空间并且呗用户程序直接访问的页面才能被交换,而被内核直接使用的内核空间的页面不能被换出。操作系统是执行的关键代码,需要保证运行的高效性和实时性,并且如果处理缺页过程所用到的内核代码或者数据被换出,那么整个内核都会面临崩溃的危险。

ucore在设计上,充分利用了PTE,正常情况下PTE由页基址、保留的可供软件使用的位Avail、全局标志位G、页属性表索引标志PAT、页尺寸标志、脏位、访问位、页级高速缓存禁用标志、页级直写标记、用户/管理标志、读/写标志、存在标志组成

我们需要注意到一点就是当一个页表项或者页目录表项的“存在”标志被清零时,操作系统可以使用该表项的其余位来存储一些其他信息,比如该页在磁盘存储系统上的位置。

而ucore中,一个页被置换到硬盘的某8个扇区(0.5KB/扇区),该PTE的最低位--present 位应该为0 (即 PTE_P 标记为空,表示虚实地址映射关系不存在),接下来的7位暂时保 留,可以用作各种扩展;而原来用来表示页帧号的高24位地址,恰好可以用来表示此页在硬 盘上的起始扇区的位置(其从第几个扇区开始)。

执行换入的时机,check_mm_struct变量这个数据结构表示了目前所有合法的虚拟内存空间集合,do_pgfault函数被调用时,会判断产生异常的地址是否属于check_mm_struct某个vma表示的合法虚拟地址空间,且保存在硬盘的swap文件中,若是则将调用swap_in函数完成页面的换入。

执行换出的时机在ucore中有两种。第一种是积极的策略,系统周期性地主动把某些不常用的页swap_out到硬盘上,即使内存并没有满。第二种是消极的策略,只有在没有空闲的物理页分配时,这时才开始查找不常用的页面swap_out。

在lab3中,换出的策略采取上述第二种。

页面替换算法的数据结构

为了表示物理页可被换出的情况,对Page数据结构进行了扩展:

struct Page {
......
list_entry_t pra_page_link;
uintptr_t pra_vaddr;
};

pra_page_link用来构造按页的第一次访问时间进行排序的一个链表,表头表示第一次访问时间最近的页,表尾表示第一次访问时间最远的页。

pra_vaddr可以用来记录此物理页对应的虚拟页起始地址。

由于ucore在lab3中仅有一个内核页表,在页被交换到磁盘上时,对应的PTE的高24位偏移*8=对应的磁盘交换的起始扇区号。

同lab2构建一个管理虚拟内存页面置换的框架swap_manager,提供的默认实现的fifo_swap_manager。

代码部分

alloc_pages函数,在lab3中进行了改动

//alloc_pages - call pmm->alloc_pages to allocate a continuous n*PAGESIZE memory
struct Page *
alloc_pages(size_t n) {struct Page *page=NULL;bool intr_flag;while (1){// 关闭中断,避免分配内存时,物理内存管理器内部的数据结构变动时被中断打断,导致数据错误local_intr_save(intr_flag);{// 分配n个物理页page = pmm_manager->alloc_pages(n);}// 恢复中断控制位local_intr_restore(intr_flag);// 满足下面之中的一个条件,就跳出while循环// page != null 表示分配成功// 如果n > 1 说明不是发生缺页异常来申请的(否则n=1)// 如果swap_init_ok == 0 说明没有开启分页模式if (page != NULL || n > 1 || swap_init_ok == 0) break;extern struct mm_struct *check_mm_struct;//cprintf("page %x, call swap_out in alloc_pages %d\n",page, n);// 尝试着将某一物理页置换到swap磁盘交换扇区中,以腾出一个新的物理页来// 如果交换成功,则理论上下一次循环,pmm_manager->alloc_pages(1)将有机会分配空闲物理页成功swap_out(check_mm_struct, n, 0);}//cprintf("n %d,get page %x, No %d in alloc_pages\n",n,page,(page-pages));return page;
}

do_pgfault函数

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// 试图从mm关联的vma链表块中查询,是否存在当前addr线性地址匹配的vma块struct vma_struct *vma = find_vma(mm, addr);// 全局页异常处理数自增1pgfault_num++;//If the addr is in the range of a mm's vma?if (vma == NULL || vma->vm_start > addr) {// 如果没有匹配到vmacprintf("not valid addr %x, and  can not find it in vma\n", addr);goto failed;}// 页访问异常错误码有32位。位0为1 表示对应物理页不存在;位1为1 表示写异常(比如写了只读页);位2为1 表示访问权限异常(比如用户态程序访问内核空间的数据)// 对3求模,主要判断bit0、bit1的值switch (error_code & 3) {default:// bit0,bit1都为1,访问的映射页表项存在,且发生的是写异常// 说明发生了缺页异常case 2: // bit0为0,bit1为1,访问的映射页表项不存在、且发生的是写异常if (!(vma->vm_flags & VM_WRITE)) {// 对应的vma块映射的虚拟内存空间是不可写的,权限校验失败cprintf("do_pgfault failed: error code flag = write AND not present, but the addr's vma cannot write\n");// 跳转failed直接返回goto failed;}// 校验通过,则说明发生了缺页异常break;case 1: // bit0为1,bit1为0,访问的映射页表项存在,且发生的是读异常(可能是访问权限异常)cprintf("do_pgfault failed: error code flag = read AND present\n");// 跳转failed直接返回goto failed;case 0: // bit0为0,bit1为0,访问的映射页表项不存在,且发生的是读异常if (!(vma->vm_flags & (VM_READ | VM_EXEC))) {// 对应的vma映射的虚拟内存空间是不可读且不可执行的cprintf("do_pgfault failed: error code flag = read AND not present, but the addr's vma cannot read or exec\n");goto failed;}// 校验通过,则说明发生了缺页异常}// 构造需要设置的缺页页表项的perm权限uint32_t perm = PTE_U;if (vma->vm_flags & VM_WRITE) {perm |= PTE_W;}// 构造需要设置的缺页页表项的线性地址(按照PGSIZE向下取整,进行页面对齐)addr = ROUNDDOWN(addr, PGSIZE);ret = -E_NO_MEM;// 用于映射的页表项指针(page table entry, pte)pte_t *ptep=NULL;// 获取addr线性地址在mm所关联页表中的页表项// 第三个参数=1 表示如果对应页表项不存在,则需要新创建这个页表项if ((ptep = get_pte(mm->pgdir, addr, 1)) == NULL) {cprintf("get_pte in do_pgfault failed\n");goto failed;}// 如果对应页表项的内容每一位都全为0,说明之前并不存在,需要设置对应的数据,进行线性地址与物理地址的映射if (*ptep == 0) { // 令pgdir指向的页表中,la线性地址对应的二级页表项与一个新分配的物理页Page进行虚实地址的映射if (pgdir_alloc_page(mm->pgdir, addr, perm) == NULL) {cprintf("pgdir_alloc_page in do_pgfault failed\n");goto failed;}}else { // 如果不是全为0,说明可能是之前被交换到了swap磁盘中if(swap_init_ok) {// 如果开启了swap磁盘虚拟内存交换机制struct Page *page=NULL;// 将addr线性地址对应的物理页数据从磁盘交换到物理内存中(令Page指针指向交换成功后的物理页)if ((ret = swap_in(mm, addr, &page)) != 0) {// swap_in返回值不为0,表示换入失败cprintf("swap_in in do_pgfault failed\n");goto failed;}    // 将交换进来的page页与mm->padir页表中对应addr的二级页表项建立映射关系(perm标识这个二级页表的各个权限位)page_insert(mm->pgdir, page, addr, perm);// 当前page是为可交换的,将其加入全局虚拟内存交换管理器的管理swap_map_swappable(mm, addr, page, 1);page->pra_vaddr = addr;}else {// 如果没有开启swap磁盘虚拟内存交换机制,但是却执行至此,则出现了问题cprintf("no swap_init_ok but ptep is %x, failed\n",*ptep);goto failed;}}// 返回0代表缺页异常处理成功ret = 0;
failed:return ret;
}

pgdir_alloc_page 建立映射虚实关系:

// 令pgdir指向的页表中,la线性地址对应的二级页表项与一个新分配的物理页Page进行虚实地址的映射
struct Page *
pgdir_alloc_page(pde_t *pgdir, uintptr_t la, uint32_t perm) {// 分配一个新的物理页用于映射lastruct Page *page = alloc_page();if (page != NULL) { // !=null 分配成功// 建立la对应二级页表项(位于pgdir页表中)与page物理页基址的映射关系if (page_insert(pgdir, page, la, perm) != 0) {// 映射失败,释放刚才分配的物理页free_page(page);return NULL;}// 如果启用了swap交换分区功能if (swap_init_ok){// 将新映射的这一个page物理页设置为可交换的,并纳入全局swap交换管理器中管理swap_map_swappable(check_mm_struct, la, page, 0);// 设置这一物理页关联的虚拟内存page->pra_vaddr=la;// 校验这个新分配出来的物理页page是否引用次数正好为1assert(page_ref(page) == 1);}}return page;
}

swap_fifo.c

static int
_fifo_init_mm(struct mm_struct *mm)
{     // 初始化先进先出链表队列list_init(&pra_list_head);mm->sm_priv = &pra_list_head;return 0;
}static int
_fifo_map_swappable(struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in)
{// 获取到mm_struct关联的先进先出链表队列list_entry_t *head=(list_entry_t*) mm->sm_priv;// 获取参数page结构对应的swap链表节点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.// 将其加入队列的头部(先进先出,最新的page页被挂载在最头上)list_add(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 qeueue,*                            then assign the value of *ptr_page to the addr of this page.*/
static int
_fifo_swap_out_victim(struct mm_struct *mm, struct Page ** ptr_page, int in_tick)
{// 获取到mm_struct关联的先进先出链表队列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)  assign the value of *ptr_page to the addr of this page/* Select the tail */// 找到头节点的前一个(双向循环链表 head的前一个节点=队列的最尾部节点)list_entry_t *le = head->prev;assert(head!=le);// 获得尾节点对应的page结构struct Page *p = le2page(le, pra_page_link);// 将le节点从先进先出链表队列中删除list_del(le);assert(p !=NULL);// 令ptr_page指向被挑选出来的page*ptr_page = p;return 0;
}

ucore lab3学习笔记整理相关推荐

  1. ucore lab1学习笔记整理

    前置知识 ucore是运行在80386这一32位x86架构的CPU,汇编采用的格式是AT&T, lab1 ucore开始执行makefile会生成磁盘映像,这个磁盘映像就是对应着现实计算机中的 ...

  2. ucore lab4学习笔记整理

    在lab4中接触的还是内核线程,内核线程只运行在内核态.在线性空间中,0-3G是用户空间,而3-4G是内核空间,所有的内核线程都是只用同一块内存,因此不需要为每个内核线程维护单独的内存空间,不像用户进 ...

  3. ucore lab2学习笔记整理

    本次实验主要完成ucore内核对物理内存的管理. ucore被启动后,需要探测系统的物理内存布局来了解哪些物理内存空间是可用的.ucore是使用e820h中断来获取内存信息,而这个中断必须在实模式下使 ...

  4. 【mysql学习笔记整理】

    /*mysql学习笔记整理*/ /*常用的数据库操作对象*/ #库的操作 #创建 #数据库的创建 USE mysql; CREATE DATABASE db_x; #删除 #删除数据库 DROP DA ...

  5. Deep Learning(深度学习)学习笔记整理系列之(五)

    Deep Learning(深度学习)学习笔记整理系列 zouxy09@qq.com http://blog.csdn.net/zouxy09 作者:Zouxy version 1.0 2013-04 ...

  6. Deep Learning(深度学习)学习笔记整理系列之(二)

    Deep Learning(深度学习)学习笔记整理系列 zouxy09@qq.com http://blog.csdn.net/zouxy09 作者:Zouxy version 1.0 2013-04 ...

  7. python eval 入门_Python学习笔记整理3之输入输出、python eval函数

    Python学习笔记整理3之输入输出.python eval函数 来源:中文源码网    浏览: 次    日期:2018年9月2日 Python学习笔记整理3之输入输出.python eval函数 ...

  8. Deep Learning(深度学习)学习笔记整理系列之(八)

     Deep Learning(深度学习)学习笔记整理系列之(八) 分类: Deep Learning 机器学习 Linux驱动2013-04-10 11:4257652人阅读评论(25)收藏举报 ...

  9. Deep Learning(深度学习)学习笔记整理系列三

    Deep Learning(深度学习)学习笔记整理系列 声明: 1)该Deep Learning的学习系列是整理自网上很大牛和机器学习专家所无私奉献的资料的.具体引用的资料请看参考文献.具体的版本声明 ...

最新文章

  1. CentOS 7.5 如何升级Git实录
  2. 清华大学大数据研究中心给您拜年啦!
  3. find 和 findindes
  4. BZOJ 1042 [HAOI2008]硬币购物
  5. ASP.NET 判断客户端是否为手机的函数
  6. GridFS删除文件代码示例
  7. 【案例分析】分布式系统的接口幂等性设计!
  8. 用ANSYS画矩形_钢轨打磨用复合砂轮磨削温度场的研究
  9. 怎么去掉Flex4生成的SWF加载时的进度条
  10. A Star寻路相关资料汇总
  11. [转载] Java标识符 数据类型 常量与变量
  12. 作者:牛新(1983-),男,博士,国防科学技术大学并行与分布处理重点实验室助理研究员...
  13. 在linux centos7上安装git
  14. html 换行_李亚涛:清除HTML所有格式并且删除换行与回车,只显示文本
  15. C#实现两个时间相减的方法
  16. (TI xDM)SSCR Module—Shared Scratch Memory
  17. “无语!只因姓True,苹果封了我的iCloud账户”
  18. Linux kmalloc/kfree 源码解读
  19. 222.完全二叉树的节点个数
  20. 计算机技术和通信技术的关系,计算机技术与通信技术的关系

热门文章

  1. NFC移动支付初探 (1)
  2. JAVA程序入门--基础知识(运算规则)
  3. 电气、电子专业英语B
  4. Python浮点数(小数)运算误差的原因和解决方法
  5. NOIP2018游记AFO后记
  6. Java Jsp+Servlet+mysql实现的图书借阅系统(系统管理员/图书管理员/读者 功能:图书信息管理、图书借阅、我的借阅记录、管理图书、用户管理、反馈)
  7. linux定时任务设置
  8. 测试点编写(2020-09-10)
  9. 浅谈Dynamics CRM开发转Dynamics AX开发的感受与差异
  10. 未能加载文件或程序集“xxx”或它的某一个依赖项。生成此程序集的运行时比当前加载的运行时新,无法加载此程序集