2020 6.s081——Lab5:Lazy page allocation

再来是千年的千年

不变是眷恋的眷恋

飞越宇宙无极限

我们永不说再见

——超兽武装

 完整代码见:SnowLegend-star/6.s081 at lazy (github.com)

Eliminate allocation from sbrk() (easy)

顾名思义,就是去掉sbrk()中调用growproc()的部分。1s完事儿。

Lazy allocation (moderate)

part02是为了修复part01挖的那个坑。代码实现在课上也是讲过的,这里粗略看一遍hints就行:

1、你可以在usertrap()中查看r_scause()的返回值是否为1315来判断该错误是否为页面错误

老实说这个hint1真是说晚了,应该在page table那个lab就提出的。到这我才真正理解scausespecstval那几个寄存器真正的用法。可以看到scause值为121315时都是页表相关的问题。

2、stval寄存器中保存了造成页面错误的虚拟地址,你可以通过r_stval()读取

3、参考***vm.c***中的uvmalloc()中的代码,那是一个sbrk()通过growproc()调用的函数。你将需要对kalloc()mappages()进行调用

模仿uvmalloc()usertrap中写一个分配页表的函数体就行。

4、使用PGROUNDDOWN(va)将出错的虚拟地址向下舍入到页面边界

这里有个坑,就是PGROUNDDOWN(va)这句的位置是有讲究的。但是part02检测不出来,part02只是测试了是否成功分配页表,而没有测试va的位置合法性。我去,原来没问题,是我冤枉了这句代码。

5、当前uvmunmap()会导致系统panic崩溃;请修改程序保证正常运行

6、如果内核崩溃,请在***kernel/kernel.asm***中查看sepc

7、使用pgtbl labvmprint函数打印页表的内容

这个hint放在part03比较好,由于part02用不到vmprint(),搞得我咋part03都忘记这个hint了。

8、如果您看到错误“incomplete type proc”,请include“spinlock.h”然后是“proc.h”

这个hint也应该放在part03

growproc()修改如下

int
growproc(int n)
{uint sz;struct proc *p = myproc();sz = p->sz;// if(n > 0){//   if((sz = uvmalloc(p->pagetable, sz, sz + n)) == 0) {//     return -1;//   }// } else if(n < 0){//   sz = uvmdealloc(p->pagetable, sz, sz + n);// }p->sz = sz + n;if(n > 0){if((sz = uvmalloc(p->pagetable, sz, sz + n)) == 0) {return -1;}} else if(n < 0){sz = uvmdealloc(p->pagetable, sz, sz + n);}return 0;
}

Lazytests and Usertests (moderate)

这个part03我觉得可以算是半步hard难度了,要修改的小地方太多了。而且这部分给的几个hints有点隐晦,让人有点摸不着头脑。还是从分析hints着手:

1、Handle negative sbrk() arguments.

这个部分我在part02就已想实现了,但是感觉在usertrap内部实现不了,就先搁置一旁。想不到part03又要实现。抱着试一试的心态我把growproc()内部的uvmdealloc()搬过来试了下,还真的是这么实现的。

2、Kill a process if it page-faults on a virtual memory address higher than any allocated with sbrk().

这句话还是比较容易理解的,问题是在哪里实现呢?我想了许久还是没什么头绪,遂依然在usertrap内部添加判断,也可行。

3、Handle the parent-to-child memory copy in fork() correctly.

这句就显得十分晦涩了,乍一看还以为是要求我们在fork()进行适当的修改。我去翻了下《CSAPP》,看到里面说使用“写时复制”后,父进程和子进程都应该被标记为只读。如果真是这样也太麻烦了,那part03绝对是hard难度。一阵挠头发现仍然没有思路后,就先跳过这句了。我想着既然有这个hint,那测试点应该会涉及到这句话的实现测试。先把大体框架完成进行测试再说。

4、Handle the case in which a process passes a valid address from sbrk() to a system call such as read or write, but the memory for that address has not yet been allocated.

这个hint的要求一目了然,不过我开始也是不知道从哪里着手。权且跳过。

5、Handle out-of-memory correctly: if kalloc() fails in the page fault handler, kill the current process.

连着被前面的hints折磨,终于是看到了个通俗易懂又易于实现的hint。我兴高采烈地掉进了一个小坑:

usertrap内部的我都是用p->killed=1来终止进程的,自以为这种写法相当的地道。但在修改vm.cwalk()时发现貌似不能用p->killed=1这个操作来终止进程,于是灵机一动想到终止进程不是可以用exit(-1)吗?后来我还测试了下能不能在usertrap中把p->killed=1exit(-1)互换。

可恶的是lazytests貌似不能检查这两者的差异,搞得我还以为上述两种方法是等价的。为后面usertests的报错埋下伏笔。

6、Handle faults on the invalid page below the user stack.

这句话也有点语焉不详,后来才发现就是检查va是不是溢出到了guard page。和hint2差不多,一个是判断是否超出上界,一个则是防止va溢出到了下界。

OK,把hints过完了一遍可以开始测试了。第一个报错如下:

 

在前面搞多了注释“panic:xxx”的操作之后,我下意识地就是来一手掩耳盗铃,直接把上述panic给注释了,发现没有效果才开始认真思考到底是什么原因。

查看出错的freewalk()发现是uvmunmap()没有彻底删除叶子表项导致这个painc。这时候就得用vmprint()检查一番了,有一说一官方不提供vmprint()我不认可。输出结果如下:

可以发现确实是有个叶子页表项没完全删除,我自己分析uvmunmap()和walk()半天不见思路,又问了下GPT是什么情况。都没得到想要的原因。在发现pagetable只有这一个异常的叶子pte后,我决定直接在freewalk()内部把这个pte删去。果然可行。

解决这个小问题后,我突然对如何解决hint3来了思路。遂在uvmcopy()来了收掩耳盗铃,注释掉了两句panic。因为父进程用sbrk()申请了堆空间之后,如果不立即使用这部分新空间,“写时分配”机制会维持PTE_V=0的状态。这时候调用fork()来生成子进程,子进程在复制父进程的空间layout就会遇到这部分父进程申请的新空间。所以得忽略PTE_V=0的判别,仍然将新空间复制到子进程的空间中。

完成上述步骤就可以通过lazytests了,接下来开始攻关usertests。遇到的第一个问题就给我难住了:

为了探究到底是什么原因导致的报错,我把sbrkmuch()仔细看了几遍。发现这个函数内部倒是没报错,是内核出了问题。而且有些测试虽然OK了,也是有类似的usertrap报错:

在发现scause无一例外都是d之后,我尝试了把scause=13也加入usertrap的页表分配中,问题由此得以解决。

else if(r_scause()==15 || r_scause()==13)

接下来就是处理最麻烦的部分了,也就是hint4:处理这种情形:进程从sbrk()向系统调用(如read或write)传递有效地址,但尚未分配该地址的内存。

 我最初的想法是在scause==8的那部分也加入页表分配的内容,发现这样会导致重映射。

然后我又想可不可以直接在sys_write()和sys_read()添加页表分配。事实证明这种方法是可行的,但是如果测试的系统调用过多,岂不是要重写每个系统调用函数,治标不治本。由于午觉没睡好有点犯困了,就去网上找了下简便方法。

处理第4种情况,即系统调用(比如write)传入的虚拟地址对应的内存并没有被分配。

首先搞清楚函数执行流程,在调用write后系统trap到内核态,执行copyin来把用户程序va处的内容复制到内核空间,此时若va处并未分配内存,walkaddr会返回0导致系统调用失败。因此我们要做的就是在walkaddr中分配内存。

处理进程将有效地址从sbrk()传递给系统调用(例如读取或写入),但尚未分配该地址的内存的情况

由于write是系统调用,在内核中访问用户地址空间出现异常不会进入usertrap()函数,内核访问用户地址空间首先要找到和虚拟地址对应的物理地址,walkaddr完成这个工作,当pte不存在或者pte无效,返回0 但是这种情况在lazy allocation中是允许的

所以针对这个问题需要在walkaddr()函数进行和usertrap中相同的操作,为访问地址分配内存并映射到页表

上面两段话可谓是醍醐灌顶,可以从根源上解决类似的问题。我们在walkaddr()内部进行和usertrap相似的操作,区别是在walkaddr()中我们操作的对象是传入的va,而非usertrap中的stval。

最后一个小问题是边界判断。

这里如果不加等号就会报下列错误。这个报错和问题代码看起来可谓是毫无关联,让我一番好找。

修改后的walkaddr()如下

uint64
walkaddr(pagetable_t pagetable, uint64 va)
{pte_t *pte;uint64 pa;if(va >= MAXVA)return 0;pte = walk(pagetable, va, 0);if(pte == 0 || (*pte & PTE_V)==0 ){struct proc *p=myproc();//由于write是系统调用,在内核中访问用户地址空间出现异常不会进入usertrap()函数uint64 mem;if(va >= p->sz)    //这里如果用low_addr > maxva就无法通过unmap测试return 0;if(va < p->trapframe->sp )  //即出错的地址位于guard pagereturn 0;mem=(uint64)kalloc();if(mem==0){return 0;}else{va = PGROUNDUP(va);       //加不加都行memset((void *)mem, 0, PGSIZE);if(mappages(p->pagetable, va, PGSIZE, mem, PTE_W|PTE_R|PTE_U)!=0){kfree((void*)mem);return 0;}}}if((*pte & PTE_U) == 0)return 0;pa = PTE2PA(*pte);return pa;}

修改后的usertrap()如下

  else if(r_scause()==15 || r_scause()==13){  //如果是出现缺页错误uint64 mem;// uint64 low_addr=PGROUNDDOWN(r_stval());//这里提早向下舍入low_addr是不是有问题啊?uint64 low_addr=r_stval();// printf("page fault %p\n", r_stval());if(low_addr >= p->sz)    //这里如果用low_addr > maxva就无法通过unmap测试// p->killed=1 ;exit(-1);if(low_addr < p->trapframe->sp )  //即出错的地址位于guard page// p->killed=1;exit(-1);mem=(uint64)kalloc();if(mem==0){// uvmdealloc(p->pagetable, low_addr, p->sz - low_addr);//用killed参数来杀死进程,而不是直接return  用kill的方式会导致  test copyinstr3: unlink(x) returned 0, not -1// p->killed=1;exit(-1);         //用exit(-1)也可以,一步到位}else{memset((void *)mem, 0, PGSIZE);low_addr=PGROUNDDOWN(low_addr);if(mappages(p->pagetable, low_addr, PGSIZE, mem, PTE_W|PTE_R|PTE_U)!=0){kfree((void*)mem);// uvmdealloc(p->pagetable, low_addr, p->sz - low_addr);// p->killed=1;   exit(-1);   }}}

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

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

相关文章

Mysql root用户远程连接失败解决方案

最近&#xff0c;踩坑云服务器通过root用户远程连接Mysql数据库失败&#xff0c;Mysql 版本为 5.7.44&#xff0c;原因如下&#xff0c;因为root用户权限过大&#xff0c;可能会有风险操作&#xff0c;可以新增其他用户来解决此问题&#xff0c;如果一定要用root用户&#xff0…

gitblit 环境搭建,服务器迁移记录

下载 Gitblit&#xff1a; http://www.gitblit.com/ JDK&#xff1a;gitblit网站显示需要jdk1.7&#xff0c;这里用的1.8。 Git&#xff1a;到官网下载最新版本安装 1). 分别安装JDK&#xff0c;Git&#xff0c;配置环境变量&#xff0c;下载并解压Gitblit 2). 创建代码仓库 …

阿里云镜像加速配置(工作中经常用到,写在此方便)

原因 由于运营商网络原因&#xff0c;会导致您拉取Docker Hub镜像变慢&#xff0c;甚至下载失败。为此&#xff0c;阿里云容器镜像服务ACR提供了官方的镜像加速器&#xff0c;从而加速官方镜像的下载。 获取镜像加速器地址 ACR会为每一个账号&#xff08;阿里云账号或RAM用户…

网络安全专用产品销售许可证查询的几种方式你知道吗?

随着网络技术的日益先进&#xff0c;网络安全事故也频发&#xff0c;因此购买网络安全专用产品&#xff0c;例如堡垒机是非常重要的。这里提醒大家要购买正规具有销售许可证的网络安全专用产品哦&#xff01;网络安全专用产品销售许可证查询的几种方式你知道吗&#xff1f; 网络…

农业收入管理实现高效策略:用友BIP收入云引领企业收款新篇章

农业收入管理的高效性对于农场或农业企业的成功至关重要。为实现高效的农业收入管理&#xff0c;可以考虑以下几点&#xff0c;并结合用友BIP收入云等工具来提升收款效率。 1. 精细化经营和现代化管理 了解土地和作物&#xff1a;农民应了解农场土地的类型和特点&#xff0c;合…

C#——break、continue、goto关键字的使用

break break是搭配循环语句使用的&#xff0c;用于跳出循环。 举例 : 当for循环执行到第5次时&#xff0c;使用break方法 跳出循环。 continue continue 语句的工作原理与 break 语句类似&#xff0c;但是 continue 语句并不会跳出整个循环&#xff0c;而是跳过本次循环继续执…

zabbix“专家坐诊”第241期问答

问题一 Q&#xff1a;华为交换机的100GE 1/0/1口的光模块收光值监测不到&#xff0c;有没有人碰到过这个问题呢&#xff1f;其他的端口都能监测到收光值&#xff0c;但是100GE 1/0/1口监测不到收光值。底层能查到&#xff0c;zabbix 6.0监控不到&#xff0c;以下是端口的报错信…

通过Dockerfile构建postgresql数据库镜像初始化表数据

通过Dockerfile构建postgresql数据库镜像初始化表数据 1.编写Dockerfile2.构建镜像 1.编写Dockerfile #基础镜像 FROM postgres:12# 环境变量&#xff0c;配置数据库用户名和密码 ENV POSTGRES_USER postgres ENV POSTGRES_PASSWORD 123456# 将初始化数据库的脚本复制到docker…

2024骨传导耳机品牌排行前五名汇总,揭晓年度最强王者骨传导机型!

骨传导耳机自问世以来&#xff0c;便迅速在蓝牙耳机市场中崭露头角&#xff0c;并且凭借特殊的传声方式和特健康的佩戴方式深得消费者的喜爱。然而&#xff0c;随着骨传导耳机逐渐热门&#xff0c;市场中品牌越来越多&#xff0c;也逐渐出现了一些劣质品牌&#xff0c;这些品牌…

小朋友分糖果-第13届蓝桥杯选拔赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第79讲。 小朋友分糖果&…

数学电路与电子工程3(MEE)—— 同步和异步时序电路、初始化触发器

同步和异步时序电路是时序电路的两大主要类型&#xff0c;它们在设计和工作原理上有着明显的不同&#xff1a; 1 同步时序电路 这类电路包含一个时钟信号&#xff0c;用来同步整个系统中数据的进展。这意味着数据的传输和处理是在时钟信号的特定边缘&#xff08;上升沿或下降沿…

爬虫之反爬思路与解决手段

阅读时间建议&#xff1a;4分钟 本篇概念比较多&#xff0c;嗯。。 0x01 反爬思路与解决手段 1、服务器反爬虫的原因 因为爬虫的访问次数高&#xff0c;浪费资源&#xff0c;公司资源被批量抓走&#xff0c;丧失竞争力&#xff0c;同时也是法律的灰色地带。 2、服务器反什么…

Cy5.5-甘氨鹅去氧胆酸荧光染料标记物:一种生物成像工具

在现代生物研究和医学诊断领域&#xff0c;荧光染料标记物扮演着举足轻重的角色。它们能够实现对生物体内特定分子或细胞的非侵入性、实时追踪&#xff0c;从而为我们揭示生命过程的奥秘提供工具。 Cy5.5-甘氨鹅去氧胆酸荧光染料标记物的结构与特性 Cy5.5-甘氨鹅去氧胆酸荧光染…

水库大坝安全监测系统打通监控数据“最后一公里”

一、概述 我国有水库8万座左右&#xff0c;其中土石坝多数&#xff0c;病险水库占水库也很多。众所周知&#xff0c;水库在防洪、兴利上具有重要的调节作用&#xff0c;如何保证水库安全&#xff0c;及合理有效的利用水资源&#xff0c;是水利建设者需要探讨的主要内容。科学技…

【Python字符串攻略】:玩转文字,编织程序的叙事艺术

文章目录 &#x1f680;一.字符串基础&#x1f308;二.查看数据类型⭐三.转化❤️四.字符串索引&#x1f6b2;五.字符串切片&#x1f3ac;六.字符串切片-步长☔七.反向切片注意事项&#x1f6b2;八.字符串&#x1f4a5;查&#x1f4a5;改&#x1f4a5;删 ❤️九.字符串拼接&…

强化用户登录接口:解决登录接口被攻击导致掉线卡顿!

一、引言 用户登录接口是任何Web应用的核心部分&#xff0c;它负责身份验证和授权流程。然而&#xff0c;这些接口也常常成为黑客攻击的目标&#xff0c;尤其是当涉及到动态请求处理时。动态请求通常指的是根据用户输入生成的请求&#xff0c;这为诸如SQL注入、XSS攻击和CSRF攻…

Ansys Mechanical|使用CABLE280和LINK180单元建立线缆模型

一. CABLE280和LINK180单元都可以用于此分析。它们都可以用来划分梁实体。下面是这两种单元的特性。 CABLE280单元 适用于仅分析单向拉伸场景&#xff0c;比如线缆 不包括剪切变形影响结果 每个节点有三个自由度&#xff1a;Ux,Uy,Uz 与属…

【Qt】 new成功,但是没有进入到构造函数。

NameTest工程中 nametest.cpp NameTest::NameTest() {pdata new privateAB; }NameTest::~NameTest() {if (pdata){privateAB *p (privateAB *)pData; //void *pdata nullptr;delete p;pdata nullptr;} }内部类&#xff1a; privateAB #include "private.h"#i…

消息队列的 6 种经典使用场景和 Kafka 架构设计原理详细解析

今天来聊一聊 Kafka 消息队列的使用场景和核心架构实现原理&#xff0c;帮助你全面了解 Kafka 其内部工作原理和设计理念。。 Apache Kafka 是一个高吞吐量、分布式的流处理平台&#xff0c;广泛应用于实时数据管道和流处理应用中。 Kafka 以其高性能、低延迟、扩展性和可靠性…