O/S使用页表硬件可以使用的许多巧妙技巧之一是延迟分配用户空间堆内存。 Xv6应用程序使用sbrk()系统调用向内核请求堆内存。 在我们给出的内核中,sbrk()分配物理内存并将其映射到进程的虚拟地址空间。 内核为大型请求分配和映射内存可能需要很长时间。 例如,考虑一个千兆字节由262,144个4096字节的页组成; 这是一个巨大的分配数量,即使每一个都是便宜的。 此外,一些程序分配的内存比它们实际使用的内存要多(例如,实现稀疏数组),或者在使用之前分配内存。 为了让sbrk()在这些情况下更快地完成,复杂的内核会延迟分配用户内存。 也就是说,sbrk()不分配物理内存,只是记住分配了哪些用户地址,并在用户页表中将这些地址标记为无效。 当进程第一次尝试使用任何给定的惰性分配内存页时,CPU会生成一个页错误,内核会通过分配物理内存、置零和映射来处理这个错误。 在本实验中,您将向xv6添加这个惰性分配特性。

在你开始编码之前,阅读xv6书的第4章(特别是4.6章),以及你可能要修改的相关文件:
kernel/trap.c
kernel/vm.c
kernel/sysproc.c

Eliminate allocation from sbrk() (easy)

要求:

您的第一个任务是从sbrk(n)系统调用实现中删除页面分配,该实现是sysproc.c中的sys_sbrk()函数。 sbrk(n)系统调用将进程的内存大小增加n个字节,然后返回新分配的区域的起始值(即旧的大小)。 新的sbrk(n)应该只增加进程的大小(myproc()->sz) n,并返回原来的大小。 它不应该分配内存——因此您应该删除对growproc()的调用(但您仍然需要增加进程的大小!)

试着猜猜这个修改的结果会是什么:什么会被打破?

进行此修改,启动xv6,并向shell输入echo hi。 你应该看到这样的东西:
init: starting sh
$ echo hi
usertrap(): unexpected scause 0x000000000000000f pid=3
sepc=0x0000000000001258 stval=0x0000000000004008
va=0x0000000000004000 pte=0x0000000000000000
panic: uvmunmap: not mapped

"usertrap():…"消息来自trap.c中的用户trap处理程序; 它捕获了一个不知道如何处理的异常。 请确保您理解为什么发生此页面错误。 “stval = 0 x0 . . 04008”表示导致页面错误的虚拟地址为0x4008。

代码:

/*
sysproc.c结合proc.c中的growproc()函数,修改sys_proc()
*/
uint64
sys_sbrk(void)
{int n;if(argint(0, &n) < 0)return -1;  struct proc* p = myproc();uint64 addr = p->sz;uint64 newSize = addr + n;if(newSize >= MAXVA)return addr;if(n < 0 ){//处理负参数if(newSize > addr){newSize = 0;uvmunmap(p->pagetable, 0, PGROUNDUP(addr)/PGSIZE, 1);}else{uvmunmap(p->pagetable, PGROUNDUP(newSize), (PGROUNDUP(addr)-PGROUNDUP(newSize))/PGSIZE, 1);}}p->sz = newSize;//if(growproc(n)<0)//  return -1;return addr;
}

结果:

Lazy allocation (moderate)

要求:

修改trap.c中的代码,在错误地址上映射新分配的物理内存页,然后返回到用户空间,让进程继续执行,从而从用户空间响应页面错误。 您应该在产生“usertrap():…”消息的printf调用之前添加代码。 修改任何其他xv6内核代码,以使echo hi工作。

提示:

通过查看usertrap()中的r_cause()是13或15,可以检查一个错误是否是页面错误。
r_stval()返回RISC-V stval寄存器,其中包含导致页面错误的虚拟地址。
从vm.c中的uvmalloc()中窃取代码,这是sbrk()调用的(通过growproc())。 你需要调用kalloc()和mapages()。
使用PGROUNDDOWN(va)将故障虚拟地址舍入到页面边界。
uvmunmap()将panic; 修改它,使其在某些页面没有映射时不会出现panic。
如果内核崩溃,在kernel/kernel.asm中查找sepc
使用pgtbl lab中的vmprint函数打印页表的内容。
如果你看到错误“incomplete type proc”,包括“spinlock.h”,然后是“proc.h”。
如果一切顺利,您的lazy allocation代码应该会导致echo hi工作。 您应该至少得到一个页面错误(因此是惰性分配),或者可能得到两个。

代码:

/*
trap.c参考vm.c中的uvmalloc(),修改usertrap()
*/
void
usertrap(void)
{int which_dev = 0;if((r_sstatus() & SSTATUS_SPP) != 0)panic("usertrap: not from user mode");// send interrupts and exceptions to kerneltrap(),// since we're now in the kernel.w_stvec((uint64)kernelvec);struct proc *p = myproc();// save user program counter.p->trapframe->epc = r_sepc();if(r_scause() == 8){// system callif(p->killed)exit(-1);// sepc points to the ecall instruction,// but we want to return to the next instruction.p->trapframe->epc += 4;// an interrupt will change sstatus &c registers,// so don't enable until done with those registers.intr_on();syscall();} else if((which_dev = devintr()) != 0){// ok} else if(r_scause() == 13 || r_scause() == 15){//检查一个错误是否是页面错误uint64 va = r_stval();//r_stval()返回RISC-V stval寄存器,其中包含导致页面错误的虚拟地址if(p->sz <= va){//地址高于sbrk申请的地址p->killed = 1;} else if(va < p->trapframe->sp){//地址低于栈顶地址 无效页p->killed = 1;} else{va = PGROUNDDOWN(va);//使用PGROUNDDOWN(va)将故障虚拟地址舍入到页面边界char *mem = kalloc();//参考vm.c的uvmalloc(),调用kalloc()和mapages()。if(mem == 0){//如果kalloc()在页面错误处理程序中失败,终止当前进程p->killed = 1;} else{memset(mem, 0, PGSIZE);if(mappages(p->pagetable, va, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U) != 0){kfree(mem);p->killed = 1;}}} }else {printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid);printf("            sepc=%p stval=%p\n", r_sepc(), r_stval());p->killed = 1;}if(p->killed)exit(-1);// give up the CPU if this is a timer interrupt.if(which_dev == 2)yield();usertrapret();
}
/*
vm.c修改uvmunmap(),页表未映射时跳过
*/
void
uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free)
{uint64 a;pte_t *pte;if((va % PGSIZE) != 0)panic("uvmunmap: not aligned");for(a = va; a < va + npages*PGSIZE; a += PGSIZE){if((pte = walk(pagetable, a, 0)) == 0)panic("uvmunmap: walk");if((*pte & PTE_V) == 0)continue;//panic("uvmunmap: not mapped"); 必须改成continue,否则可能进入do_freeif(PTE_FLAGS(*pte) == PTE_V)panic("uvmunmap: not a leaf");if(do_free){uint64 pa = PTE2PA(*pte);kfree((void*)pa);}*pte = 0;}
}

结果:

Lazytests and Usertests (moderate)

要求:

我们已经为您提供了lazytests,这是一个xv6用户程序,用于测试一些可能会给lazy allocation分配器带来压力的特定情况。 修改内核代码,以便所有的惰性测试和用户测试都能通过。

处理负的sbrk()参数。
如果进程在高于sbrk()分配的任何虚拟内存地址上发生页故障,则终止进程。
正确处理fork()中的父到子内存副本。
处理进程将有效地址从sbrk()传递给系统调用(例如read或write),但尚未为该地址分配内存的情况。
正确处理内存不足:如果kalloc()在页面错误处理程序中失败,终止当前进程。
处理用户堆栈下面的无效页上的错误。
如果你的内核通过了lazytests和usertests,你的解决方案是可以接受的:

代码:

/*
处理负的sbrk()参数。  见5.1修改的sysproc.c第55行后
*//*
如果进程在高于sbrk()分配的任何虚拟内存地址上发生页故障,则终止进程。 见5.2修改的trap.c第74行后
*/ /*
正确处理内存不足:如果kalloc()在页面错误处理程序中失败,终止当前进程。  见5.2修改的trap.c第81行后
*//*
处理用户堆栈下面的无效页上的错误。  见5.2修改的trap.c第76行后
*/
/*
vm.c正确处理fork()中的父到子内存copy。
修改uvmcopy(),页表不存在或未映射直接跳过
类似5.2中对vm.c中uvmunmap()的处理。
*/
int
uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
{pte_t *pte;uint64 pa, i;uint flags;char *mem;for(i = 0; i < sz; i += PGSIZE){if((pte = walk(old, i, 0)) == 0)continue;//panic("uvmcopy: pte should exist");if((*pte & PTE_V) == 0)continue;//panic("uvmcopy: page not present");pa = PTE2PA(*pte);flags = PTE_FLAGS(*pte);if((mem = kalloc()) == 0)goto err;memmove(mem, (char*)pa, PGSIZE);if(mappages(new, i, PGSIZE, (uint64)mem, flags) != 0){kfree(mem);goto err;}}return 0;err:uvmunmap(new, 0, i / PGSIZE, 1);return -1;
}
/*
vm.v处理进程将有效地址从sbrk()传递给系统调用(例如read或write),但尚未为该地址分配内存的情况。
参考vm.c中uvmalloc()
类似5.2中对vm.c中usertrap()的处理。
*/
#include "spinlock.h"//添加头文件,要按照顺序
#include "proc.h"int
copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
{uint64 n, va0, pa0;struct proc* p = myproc();while(len > 0){va0 = PGROUNDDOWN(srcva);pa0 = walkaddr(pagetable, va0);if(pa0 == 0){if(p->sz <= va0)return -1;uint64 mem = kalloc();if(mem == 0){return -1;}memset(mem, 0, PGSIZE);if(mappages(pagetable, va0, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U) != 0){kfree(mem);return -1;}pa0 = mem;}  n = PGSIZE - (srcva - va0);if(n > len)n = len;memmove(dst, (void *)(pa0 + (srcva - va0)), n);len -= n;dst += n;srcva = va0 + PGSIZE;}return 0;
}int
copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
{uint64 n, va0, pa0;struct proc* p = myproc();while(len > 0){va0 = PGROUNDDOWN(dstva);pa0 = walkaddr(pagetable, va0);if(pa0 == 0){if(p->sz <= va0)return -1;uint64 mem = kalloc();if(mem == 0){return -1;}memset(mem, 0, PGSIZE);if(mappages(pagetable, va0, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U) != 0){kfree(mem);return -1;}pa0 = mem;}  n = PGSIZE - (dstva - va0);if(n > len)n = len;memmove((void *)(pa0 + (srcva - va0)), src, n);len -= n;src += n;dstva = va0 + PGSIZE;}return 0;
}

结果:


操作系统实验Mit6.S081笔记 Lab5: Lazy allocation相关推荐

  1. MIT6.S081学习总结-lab5:lazy page allocation

    lab5 是lazy page allocationi相关,主要解决页面错误问题.O/S可以对页表硬件使用的许多巧妙技巧之一是延迟分配用户空间堆内存.Xv6应用程序使用sbrk()系统调用向内核请求堆 ...

  2. 操作系统MIT6.S081:[xv6参考手册第4章]->Trap与系统调用

    本系列文章为MIT6.S081的学习笔记,包含了参考手册.课程.实验三部分的内容,前面的系列文章链接如下 操作系统MIT6.S081:[xv6参考手册第1章]->操作系统接口 操作系统MIT6. ...

  3. 操作系统MIT6.S081:P7->Interrupts

    本系列文章为MIT6.S081的学习笔记,包含了参考手册.课程.实验三部分的内容,前面的系列文章链接如下 操作系统MIT6.S081:[xv6参考手册第1章]->操作系统接口 操作系统MIT6. ...

  4. 操作系统MIT6.S081:Lab4->Trap

    本系列文章为MIT6.S081的学习笔记,包含了参考手册.课程.实验三部分的内容,前面的系列文章链接如下 操作系统MIT6.S081:[xv6参考手册第1章]->操作系统接口 操作系统MIT6. ...

  5. 操作系统-MIT6.S081学习

    操作系统 Lec01 1.2 操作系统结构 以距型表示: 硬件资源包括:CPU.内存.磁盘.网卡,在底层 架构上层,需要运行各种的应用程序,例如 文本编译器(vi).c编译器(cc),cli 存在的 ...

  6. EDA实验课课程笔记(一)——linux操作系统及linux下的基本指令

    EDA实验课课程笔记(一)--linux操作系统及linux下的基本指令 实验目标 1,了解Linux系统的基本特点 2,掌握Linux的常用命令 3,掌握Linux的命令输入技巧 实验内容 1,基本 ...

  7. 操作系统实验:页面置换算法的模拟实现及命中率对比(学习笔记)

    操作系统实验:页面置换算法的模拟实现及命中率对比(学习笔记) 题目要求 输入要求 输出要求 编程平台 实验成果 开始模拟 错误输入 退出程序 代码实现 抽象数据类型定义 指令地址流生成 指令地址流到页 ...

  8. ucore操作系统实验笔记 - Lab1

    最近一直都在跟清华大学的操作系统课程,这个课程最大的特点是有一系列可以实战的操作系统实验.这些实验总共有8个,我在这里记录实验中的一些心得和总结. Task1 这个Task主要是为了熟悉Makfile ...

  9. 北航linux内核编译及烧录实验报告,北航操作系统实验Lab1笔记

    Loading... # 北航操作系统实验Lab1 ## Exercise 1.1 - **修改交叉编译路径为 `/OSLAB/compiler/usr/bin/mips_4KC-`** ![ex1_ ...

最新文章

  1. GOF23设计模式(结构型模式)代理模式~
  2. 今日 Paper | 多人姿势估计;对话框语义分析;无监督语义分析;自然语言处理工具包等
  3. c++枚举类型(二) c++11 枚举类
  4. 关于l ibrary not found for -lz.1.2.3 编译错误
  5. [网络安全自学篇] 二十九.文件上传和IIS6.0解析问题及防御原理(二)
  6. Android开发之百度地图距离判断(判断定位位置是否在圆的范围内)
  7. 【Verilog HDL】第四章 模块的端口连接规则——污水处理模型
  8. 分层结构,协议,接口,服务
  9. 读《世界是数字的》有感③
  10. php源码 辅助发卡_【程序源码】PHP自动化售货发卡网源码
  11. CAM350 使用,典型做钢网
  12. Ultra Fast Deep Lane Detection with Hybrid Anchor Driven Ordinal Classification论文解读
  13. 《深度学习之美》推荐序:通俗也是一种美德
  14. 苹果safari浏览器的正确使用方法
  15. 大数据全样而非抽样原理_一文带你了解什么是大数据
  16. unity疯狂牧场完整项目源码 - Frenzy Farming time management game kit V1.0
  17. 区块链技术以太坊简介
  18. C语言:浙大版《C语言程序设计(第3版)》题目集 习题5-6 使用函数输出水仙花数 (20 分)
  19. java计算机毕业设计精品旅游项目管理系统MyBatis+系统+LW文档+源码+调试部署
  20. ubuntu 编译opencv

热门文章

  1. 等待事件系列(1)--User I/O类型
  2. 博弈六子棋安徽省2020省三源码
  3. STP生成树(2020.7.20)
  4. “电脑诊所”自助服务,小问题“不求人”
  5. python 线性回归 技术方案亮点_python线性回归实战案例
  6. 哈啰出行的商业版图,早已不止是车
  7. ros入门(七)--ros接入sick激光雷达
  8. LINUX GPIO 基操之驱动篇
  9. 腾讯云服务器可用区什么意思?
  10. zotero突然不能翻译了