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()的返回值是否为13或15来判断该错误是否为页面错误

老实说这个hint1真是说晚了,应该在page table那个lab就提出的。到这我才真正理解scause、spec、stval那几个寄存器真正的用法。可以看到scause值为12、13、15时都是页表相关的问题。
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 lab的vmprint函数打印页表的内容
这个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.c的walk()时发现貌似不能用p->killed=1这个操作来终止进程,于是灵机一动想到终止进程不是可以用exit(-1)吗?后来我还测试了下能不能在usertrap中把p->killed=1和exit(-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); }}}
相关文章:
2020 6.s081——Lab5:Lazy page allocation
再来是千年的千年 不变是眷恋的眷恋 飞越宇宙无极限 我们永不说再见 ——超兽武装 完整代码见:SnowLegend-star/6.s081 at lazy (github.com) Eliminate allocation from sbrk() (easy) 顾名思义,就是去掉sbrk()中调用growproc()的部分。1s完事儿。 Laz…...
华为认证学习笔记:生成树
以太网交换网络中为了进行链路备份,提高网络可靠性,通常会使用冗余链路。但是使用冗余链路会在交换网络上产生环路,引发广播风暴以及MAC地址表不稳定等故障现象,从而导致用户通信质量较差,甚至通信中断。为解决交换网络…...
leetcode 97.交错字符串
思路:LCS 其实也是同一个类型的题目,一般涉及到这种子序列的字符串问题的时候,状态的设置基本上都应该是以...结尾为状态的。这里同样,设置用dp[i][j]为s1,s2字符以i,j结尾能否拼接成s3[ij]。 那么,首先就…...
The Missing Semester ( Shell 工具和脚本 和 Vim)
管道符号 (1)管道符号 | 将前一个命令的输出作为下一个命令的输入 例如: 以下为 ./semester输出中提取包含 "Last-Modified" 的行并写入文件 last-modified.txt./semester | grep "Last-Modified" > ~/last-modif…...
【Uniapp微信小程序】自定义水印相机、微信小程序地点打卡相机
效果图 template 下方的image图片自行寻找替换! <template><view><camerav-if"!tempImagePath && cameraHeight ! 0":resolution"high":frame-size"large":device-position"device":flash"f…...
SimPO: Simple Preference Optimization with a Reference-Free Reward
https://github.com/princeton-nlp/SimPO 简单代码 class simpo(paddle.nn.Layer):def __init__(self):super(OrPoLoss, self).__init__()self.loss paddle.nn.CrossEntropyLoss()def forward(self,neg_logit, neg_lab, pos_logit, pos_lab,beta,gamma):neg_logit paddle.n…...
CDH6.3.2安装文档
前置环境: 操作系统: CentOS Linux release 7.7 java JDK : 1.8.0_231 1、准备工作 准备以下安装包: Cloudera Manager: cloudera-manager-agent-6.3.1-1466458.el7.x86_64.rpm cloudera-manager-daemons-6.3.1-1466458.el…...
Java实战入门:深入解析Java中的 `Arrays.sort()` 方法
文章目录 一、方法定义参数说明返回值 二、使用场景三、实现原理四、示例代码示例一:对整型数组排序示例二:对字符串数组排序示例三:对自定义对象数组排序 五、注意事项六、总结 在Java编程中,Arrays.sort() 方法是一个非常常用的…...
JavaScript的垃圾回收机制
No.内容链接1Openlayers 【入门教程】 - 【源代码示例300】 2Leaflet 【入门教程】 - 【源代码图文示例 150】 3Cesium 【入门教程】 - 【源代码图文示例200】 4MapboxGL【入门教程】 - 【源代码图文示例150】 5前端就业宝典 【面试题详细答案 1000】 文章目录 一、垃圾…...
小程序使用Canvas设置文字竖向排列
在需要使用的js页面引入js文件,传入对应参数即可 /** * 文本竖向排列 */ function drawTextVertical(context, text, x, y) {var arrText text.split();var arrWidth arrText.map(function (letter) {return 26; // 字体间距,需要自定义可以自己加参数,根据传入参数进行…...
GPT-4o:重塑人机交互的未来
一个愿意伫立在巨人肩膀上的农民...... 一、推出 在人工智能(AI)领域,自然语言处理(NLP)技术一直被视为连接人类与机器的桥梁。近年来,随着深度学习技术的快速发展,NLP领域迎来了前所未有的变革…...
大语言模型拆解——Tokenizer
1. 认识Tokenizer 1.1 为什么要有tokenizer? 计算机是无法理解人类语言的,它只会进行0和1的二进制计算。但是呢,大语言模型就是通过二进制计算,让你感觉计算机理解了人类语言。 举个例子:单1,双2&#x…...
Linux自动挂载服务autofs讲解
1.产生原因 2.配置文件讲解 总结:配置客户端,先构思好要挂载的目录如:/abc/cb 然后在autofs.master中编辑: /abc(要挂载的主目录) /etc/qwe(在这个文件里去找要挂载的副目录,这个名…...
堆结构知识点复习——玩转堆结构
前言:堆算是一种相对简单的数据结构, 本篇文章将详细的讲解堆中的知识点, 包括那些我们第一次学习堆的时候容易忽略的内容, 本篇文章会作为重点详细提到。 本篇内容适合已经学完C语言数组和函数部分的友友们观看。 目录 什么是堆 建堆算法…...
JS数据类型运算符标准库
目录 数据类型运算符标准库对象Object对象属性描述对象Array对象包装对象Boolean对象Number对象String对象Math对象Date对象...
单片机之从C语言基础到专家编程 - 4 C语言基础 - 4.13数组
C语言中,有一类数据结构,它可以存储一组相同类型的元素,并且可以通过索引访问这些元素,没错,这类数据结构就是数组。数组可以说是C语言中非常重要的数据结构之一了。使用数组可以是程序逻辑更加清晰,也更加…...
【码银送书第二十期】《游戏运营与出海实战:策略、方法与技巧》
市面上的游戏品种繁杂,琳琅满目,它们是如何在历史的长河中逐步演变成今天的模式的呢?接下来,我们先回顾游戏的发展史,然后按照时间轴来叙述游戏运营的兴起。 作者:艾小米 本文经机械工业出版社授权转载&a…...
String 类
目录: 一. 认识 String 类 二. String 类的基本用法 三. String对象的比较 四.字符串的不可变性 五. 认识 StringBuffer 和 StringBuilder 一. 认识 String 类: 在C语言中已经涉及到字符串了,但是在C语言中要表示字符串只能使用字符数组或者…...
Chromebook Plus中添加了Gemini?
Chromebook Plus中添加了Gemini? 前言 就在5月29日,谷歌宣布了一项重大更新,将其Gemini人工智能技术集成到Chromebook Plus笔记本电脑中。这项技术此前已应用于谷歌的其他设备。华硕和惠普已经在市场上销售的Chromebook Plus机型,…...
Git Large File Storage (LFS) 的安装与使用
Git Large File Storage [LFS] 的安装与使用 1. An open source Git extension for versioning large files2. Installing on Linux using packagecloud3. Getting Started4. Error: Failed to call git rev-parse --git-dir: exit status 128References 1. An open source Git…...
简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...
淘宝扭蛋机小程序系统开发:打造互动性强的购物平台
淘宝扭蛋机小程序系统的开发,旨在打造一个互动性强的购物平台,让用户在购物的同时,能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机,实现旋转、抽拉等动作,增…...
Python 高效图像帧提取与视频编码:实战指南
Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...
【深尚想】TPS54618CQRTERQ1汽车级同步降压转换器电源芯片全面解析
1. 元器件定义与技术特点 TPS54618CQRTERQ1 是德州仪器(TI)推出的一款 汽车级同步降压转换器(DC-DC开关稳压器),属于高性能电源管理芯片。核心特性包括: 输入电压范围:2.95V–6V,输…...
Appium下载安装配置保姆教程(图文详解)
目录 一、Appium软件介绍 1.特点 2.工作原理 3.应用场景 二、环境准备 安装 Node.js 安装 Appium 安装 JDK 安装 Android SDK 安装Python及依赖包 三、安装教程 1.Node.js安装 1.1.下载Node 1.2.安装程序 1.3.配置npm仓储和缓存 1.4. 配置环境 1.5.测试Node.j…...
