MIT 6s081 lab 5: xv6 lazy page allocation

Page faults

Basic

通过page fault可以实现一系列的虚拟内存功能:

  • lazy allocation
  • copy-on-write fork
  • demand paging
  • memory mapped files

虚拟内存的两个主要的优点:

1、隔离性:每个应用程序拥有自己的地址空间,因此不可能修改其他应用程序的内存数据,同时用户空间和内核空间也具备隔离性

2、抽象,处理器和指令可以使用虚拟地址,内核会定义从虚拟地址到物理地址的映射关系

page fault可以使得地址映射关系变得动态,内核可以更新page table,内核将会有巨大的灵活性

内核使用三个重要信息来响应page fault:

  • 出错的虚拟内存地址(存放在STVAL寄存器中)
  • 出错的原因(存放在SCAUSE寄存器中)
  • 引起page fault时的程序计数器值,代表page fault发生的位置(存放在SEPC寄存器)

page fault同样使用trap机制来进入内核空间。

Lazy page allocation

sbrk是XV6提供的系统调用,它使得用户应用程序能扩大自己的heap。当一个应用程序启动的时候,sbrk指向的是heap的最底端,同时也是stack的最顶端。这个位置通过代表进程的数据结构中的sz字段表示.

在这里插入图片描述

当sbrk实际发生或者被调用的时候,内核会分配一些物理内存,并将这些内存映射到用户应用程序的地址空间,然后将内存内容初始化为0,再返回sbrk系统调用。这样,应用程序可以通过多次sbrk系统调用来增加它所需要的内存。类似的,应用程序还可以通过给sbrk传入负数作为参数,来减少或者压缩它的地址空间。

在XV6中,sbrk的实现默认是eager allocation。这表示了,一旦调用了sbrk,内核会立即分配应用程序所需要的物理内存。但是实际上,对于应用程序来说很难预测自己需要多少内存,所以通常来说,应用程序倾向于申请多于自己所需要的内存。

利用lazy allocation,核心思想非常简单,sbrk系统调基本上不做任何事情,唯一需要做的事情就是提升p->sz,将p->sz增加n,其中n是需要新分配的内存page数量。但是内核在这个时间点并不会分配任何物理内存。之后在某个时间点,应用程序使用到了新申请的那部分内存,这时会触发page fault,这时再分配需要的物理内存,然后再去执行

lab5:xv6 lazy page allocation

作业地址:Lab: xv6 lazy page allocation (mit.edu)

Eliminate allocation from sbrk() (easy)

根据提示,修改sys_sbrk函数,删去growproc(n)的调用,但要修改myproc()->sz

uint64
sys_sbrk(void)
{int addr;int n;if(argint(0, &n) < 0)return -1;addr = myproc()->sz;// if(growproc(n) < 0)//   return -1;myproc()->sz += n;return addr;
}

Lazy allocation (moderate)

根据指导书提示,修改usertrap函数,参考uvmalloc()函数,添加对page fault的处理,注意要使用PGROUNDDOWN(va)来对齐,建立完整的一页

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}  // lab5 add beginelse if(r_scause() == 13 || r_scause() == 15) // page fault {uint64 va = r_stval(); // get the virtual address that caused the page fault.// printf("page fault %p\n", va);if(va <= p->sz) {char * pa = kalloc(); // alloc physial memory ,分配一页物理内存if(pa == 0){ // 申请失败p->killed = 1; // 杀死进程}else{memset(pa, 0, PGSIZE); //清空物理内存if(mappages(p->pagetable, PGROUNDDOWN(va), PGSIZE, (uint64)pa, PTE_W|PTE_R|PTE_U) != 0){ //建立从va下取整开始一页的映射kfree(pa); // 分配失败,释放物理内存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();
}

Lazytests and Usertests (moderate)

1、首先要处理sbrk传入的参数为负数的情况

uint64
sys_sbrk(void)
{int addr;int n;if(argint(0, &n) < 0)return -1;addr = myproc()->sz;if(n < 0){ //如果删除内存,就直接删除,但有可能之前就没有分配if(growproc(n) < 0) // 修改uvmunmap函数,walk不到也不管,没有建立映射也不管return -1;}else{myproc()->sz += n;}return addr;
}

同时要修改uvmunmap函数,删除的部分内存**有可能之前就没有分配对应的页表,也可能分配了页表,但没有插入页表项,**所以需要把panic("uvmunmap: walk")panic("uvmunmap: not mapped")注释掉,改成continue

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");continue;if((*pte & PTE_V) == 0)// panic("uvmunmap: not mapped");continue;if(PTE_FLAGS(*pte) == PTE_V)panic("uvmunmap: not a leaf");if(do_free){uint64 pa = PTE2PA(*pte);kfree((void*)pa);}*pte = 0;}
}

2、当page-faults发生的虚拟地址比之前sbrk申请的内存地址还要高,说明出现了错误,需要杀死进程,同时需要处理虚拟地址空间小于最开始栈顶指针的位置的情况,这种情况也需要杀死进程。

修改usertrap()函数:

else if(r_scause() == 13 || r_scause() == 15) // page fault {uint64 va = r_stval(); // get the virtual address that caused the page fault.// printf("page fault %p\n", va);if(va <= p->sz && va >= p->trapframe->sp) {char * pa = kalloc(); // alloc physial memory ,分配一页物理内存if(pa == 0){ // 申请失败p->killed = 1; // 杀死进程}else{memset(pa, 0, PGSIZE); //清空物理内存if(mappages(p->pagetable, PGROUNDDOWN(va), PGSIZE, (uint64)pa, PTE_W|PTE_R|PTE_U) != 0){ //建立从va下取整开始一页的映射kfree(pa); // 分配失败,释放物理内存p->killed = 1;}}}else p->killed = 1; // 此时出现异常的va高于p->sz,说明有问题,直接kill process}

3、需要处理fork时,对用户进程的页表的处理,修改uvmcopy函数(和修改uvmunmap函数类似),**有可能有的虚拟地址并没有建立页表,也有可能有的地址建立了页表,但没有添加页表项。**因此将panic("uvmcopy: pte should exist")panic("uvmcopy: page not present")改为continue

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)// panic("uvmcopy: pte should exist");continue;if((*pte & PTE_V) == 0)// panic("uvmcopy: page not present");continue;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;
}

4、需要处理read、write、pipe的系统调用,传入的虚拟地址可能是有效的(高于一开始的栈顶指针,低于sbrk分配的内存地址),但并没有分配对应的物理内存,此时需要及时地进行分配。

修改sys_read

uint64
sys_read(void)
{struct file *f;int n;uint64 p;if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argaddr(1, &p) < 0)return -1;// 判断一下addr虚拟地址对应的物理地址空间是否被申请,如果没有被申请,则申请。if(p <= myproc()->sz && p >= myproc()->trapframe->sp && walkaddr(myproc()->pagetable, p) == 0) {printf("debug: %p %d\n", p, n);// 如果这个虚拟地址p没有映射,那就建立映射char * pa = kalloc(); // alloc physial memory ,分配一页物理内存if(pa == 0){ // 申请失败myproc()->killed = 1;return -1;}else{memset(pa, 0, PGSIZE); //清空物理内存if(mappages(myproc()->pagetable, PGROUNDDOWN(p), PGSIZE, (uint64)pa, PTE_W|PTE_R|PTE_U) != 0){ //建立从va下取整开始一页的映射kfree(pa); // 分配失败,释放物理内存myproc()->killed = 1;return -1;}}}return fileread(f, p, n);
}

修改sys_write

uint64
sys_write(void)
{struct file *f;int n;uint64 p;if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argaddr(1, &p) < 0)return -1;if(p <= myproc()->sz && p >= myproc()->trapframe->sp && walkaddr(myproc()->pagetable, p) == 0) {// printf("debug: %p %d\n", p, n);// 如果这个虚拟地址p没有映射,那就建立映射char * pa = kalloc(); // alloc physial memory ,分配一页物理内存if(pa == 0){ // 申请失败myproc()->killed = 1;return -1;}else{memset(pa, 0, PGSIZE); //清空物理内存if(mappages(myproc()->pagetable, PGROUNDDOWN(p), PGSIZE, (uint64)pa, PTE_W|PTE_R|PTE_U) != 0){ //建立从va下取整开始一页的映射kfree(pa); // 分配失败,释放物理内存myproc()->killed = 1;return -1;}}}return filewrite(f, p, n);
}

修改sys_pipe

uint64
sys_pipe(void)
{uint64 fdarray; // user pointer to array of two integersstruct file *rf, *wf;int fd0, fd1;struct proc *p = myproc();if(argaddr(0, &fdarray) < 0)return -1;// 这里同理,要加判断if(fdarray <= myproc()->sz && fdarray >= myproc()->trapframe->sp && walkaddr(myproc()->pagetable, fdarray) == 0) {// printf("debug: %p %d\n", p, n);// 如果这个虚拟地址p没有映射,那就建立映射char * pa = kalloc(); // alloc physial memory ,分配一页物理内存if(pa == 0){ // 申请失败myproc()->killed = 1;return -1;}else{memset(pa, 0, PGSIZE); //清空物理内存if(mappages(myproc()->pagetable, PGROUNDDOWN(fdarray), PGSIZE, (uint64)pa, PTE_W|PTE_R|PTE_U) != 0){ //建立从va下取整开始一页的映射kfree(pa); // 分配失败,释放物理内存myproc()->killed = 1;return -1;}}}
...
}

测试:

== Test running lazytests == 
$ make qemu-gdb
(4.5s) 
== Test   lazy: map == lazy: map: OK 
== Test   lazy: unmap == lazy: unmap: OK 
== Test usertests == 
$ make qemu-gdb
(78.4s) 
== Test   usertests: pgbug == usertests: pgbug: OK 
...
...
== Test   usertests: forktest == usertests: forktest: OK 
== Test time == 
time: OK 
Score: 119/119

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/624491.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Dart】=> [06] Dart初体验-类Class-构造函数-继承-mixin-异步编程-链式调用-泛型-异常

目录 能够定义并使用Dart的类类的定义构造函数私有属性和方法继承mixin异步编程FutureFuture链式调用async - awaitdynamic类型泛型异常 能够定义并使用Dart的类 Dart是一门面向对象的编程语言&#xff0c;所有的对象都是类的实例 通过类我们可以对数据和方法进行封装复用 学习…

(2023版)斯坦福CS231n学习笔记:DL与CV教程 (1) | 引言与知识基础

前言 &#x1f4da; 笔记专栏&#xff1a;斯坦福CS231N&#xff1a;面向视觉识别的卷积神经网络&#xff08;23&#xff09;&#x1f517; 课程链接&#xff1a;https://www.bilibili.com/video/BV1xV411R7i5&#x1f4bb; CS231n: 深度学习计算机视觉&#xff08;2017&#xf…

NLP论文阅读记录 - 2022 | WOS 用于摘要法律文本的有效深度学习方法

文章目录 前言0、论文摘要一、Introduction1.1目标问题 二.相关工作三.本文方法四 实验效果4.1数据集4.2 对比模型4.3实施细节4.4评估指标4.5 实验结果4.6 细粒度分析 五 总结 前言 Effective deep learning approaches for summarization of legal texts&#xff08;22&#x…

Linux -- Nginx服务基础

4.1Nginx服务基础 Nginx(发音为[engine x])专为性能优化而开发&#xff0c;其最知名的优点是它的稳定性和低系统资源消 耗&#xff0c;以及对HTTP并发连接的高处理能力&#xff08;单台物理服务器可支持30000~50000个并发请求&#xff09;&#xff0c;正因 为如此&#xff0c;…

vscode中关于python的一些常用配置

文章目录 python cv2 提示配置第一步 配置提示信息第二部 重启vs 可能还不行&#xff0c;那就重新安装以下opencv-python 配置pytest还是如上&#xff0c;将下入的位置打开编写测试用例 配置跨文件import在工作目录中新建一个.env文件输入内容如下打开.vscode中的setting.json …

目标识别跟踪模块Tofu3

Tofu系列提供了适应不同目标、不同速率的识别跟踪模块产品系列&#xff0c;主要包括Tofu3&#xff0c;4&#xff0c;5&#xff0c;S和其他零配件&#xff0c;可以适配BT.656,Cameralink&#xff0c;网络等不同接口和协议的热红外、可见光视频。 Tofu3 是多波段视频物体识别跟踪…

GL Logger和CANFDLog-OTL-128两款记录仪都是如何实现高效的报文录制的?

GL Logger是Vector推出的记录CAN/CAN FD、LIN、FlexRay和MOST数据通信的工具。以GL2400为例带着大家一步步地实现路试过程中通过整车OBD口进行CAN/CANFD报文的录制。 Step1 设备配置 设备配置即设备录制方式、录制内容、设备休眠唤醒策略等。 ▷ 打开Vector Logger Configurat…

UML-用例图

提示&#xff1a;用例图是软件建模的开始&#xff0c;软件建模中的其他图形都将以用例图为依据。用例图列举了系统所需要实现的所有功能&#xff0c;除了用于软件开发的需求分析阶段&#xff0c;也可用于软件的系统测试阶段。 UML-用例图 一、用例图的基础知识1.用例图的构成元…

使用numpy处理图片——滤镜

大纲 3维数组切分打平重组法深度切分法 3维数组堆叠 我们在用手机拍照片时&#xff0c;往往会对照片进行滤镜处理&#xff0c;从而让照片更加美观。本文我们将实现几种滤镜效果——去除所有像素中的某一种原色&#xff0c;形成只有红绿、红蓝和绿蓝原色的照片。 为了突出色彩丰…

定制服务器有什么优势优点?

定制服务器是指在根据用户的需求和业务特点&#xff0c;专门设计和制造的服务器。与标准服务器相比&#xff0c;定制服务器具有以下优势和优点&#xff1a; 更好的性能&#xff1a;定制服务器可以针对特定应用进行优化&#xff0c;从而提高服务器的性能。由于定制服务器不需要…

Win和Mac系统重置系统方法

注意&#xff1a;重置系统前&#xff0c;请备份好系统盘资料到其他盘符&#xff01;重置系统将会删除应用和系统设置&#xff0c;甚至用户文件&#xff0c;还原为出厂设置模式。 Windows重置系统操作方法。&#xff08;目前支持WIN8&#xff0c;WIN10&#xff0c;WIN11&#x…

Linux系统使用docker部署Geoserver(简单粗暴,复制即用)

1、拉取镜像 docker pull kartoza/geoserver:2.20.32、创建数据挂载目录 # 统一管理Docker容器的数据文件,geoserver mkdir -p /mydata/geoserver# 创建geoserver的挂载数据目录 mkdir -p /mydata/geoserver/data_dir# 创建geoserver的挂载数据目录&#xff0c;存放shp数据 m…

【数据库原理】(24)数据库安全性策略

数据库安全性是数据库管理系统&#xff08;DBMS&#xff09;中一个至关重要的方面。它指的是保护数据库免受非授权访问和恶意操作&#xff0c;包括数据泄露、修改、破坏等。 多层安全模型 在典型的计算机系统安全模型中&#xff0c;安全措施被设置在不同层级&#xff1a; 应用…

Unity ComputeShader 使用GPU快速计算复杂问题

Unity ComputeShader 使用GPU快速计算复杂问题 前言项目创建ComputeShader编写CompturShader创建Unity代码场景布置运行场景 参考 前言 遇到一个问题&#xff0c;需要大量的计算&#xff0c;在Unity中直接写会长时间的阻塞主线程&#xff0c;正好使用ComputeShader让GPU来帮我…

海格里斯HEGERLS仓储货架生产厂家|载荷1.5T运行速度1.7~2m/s的智能四向穿梭车系统

四向穿梭车立体库是近年来出现的一种智能型密集系统&#xff0c;通过使用四向穿梭车在货架的水平和纵向轨道上运行来搬运货物&#xff0c;一台四向穿梭车就能完成货物的搬运工作&#xff0c;大大提高了工作效率。同时配合提升机、自动化仓库管理系统(WMS)和仓库调度系统(WCS)&a…

使用WAF防御网络上的隐蔽威胁之SSRF攻击

服务器端请求伪造&#xff08;SSRF&#xff09;攻击是一种常见的网络安全威胁&#xff0c;它允许攻击者诱使服务器执行恶意请求。与跨站请求伪造&#xff08;CSRF&#xff09;相比&#xff0c;SSRF攻击针对的是服务器而不是用户。了解SSRF攻击的工作原理、如何防御它&#xff0…

手写OpenFeign(简易版)

Remoting组件实现 1. 前言2. 原理说明3. 远程调用组件实现---自定义注解3.1 添加Spring依赖3.2 编写EnableRemoting注解3.3 编写RemoteClient注解3.4 编写GetMapping注解 4. 远程调用组件实现---生成代理类4.1 编写自定义BeanDefinition注册器4.2 编写自定义包扫描器4.3 编写Fa…

JVM初识

什么是JVM&#xff1f; JVM全称是Java Virtual Machine&#xff0c;中文译名Java虚拟机。 JVM本质上是一个运行在计算机上的程序&#xff0c;他的职责是运行Java字节码文件。 JVM的功能 jvm的功能主要分为三部分&#xff1a; 解释和运行 对字节码文件中的指令&#xff0c;实…

vue实现导出+ 样式修改

1.安装插件 npm xlsx-style ^0.18.5 npm install xlsx -S ^0.8.13 2. 修改代码 node_modules里面找到 以下位置xlsx.js 搜索 write_ws_xml_data 替换成以下代码 function write_ws_xml_data(ws, opts, idx, wb) {var o [], r [], range safe_decode_range(ws[!ref]…

搭建储能监控云平台:实现能源管理的智能化

搭建储能监控云平台&#xff1a;实现能源管理的智能化 在全球能源变革的大背景下&#xff0c;储能技术的重要性日益凸显。储能监控云平台作为能源管理的智能解决方案&#xff0c;可以为企业提供全方位的储能系统监控与数据分析&#xff0c;提高能源利用率&#xff0c;降低能源成…