news 2026/7/5 13:26:18

xv6 lab5 lazy

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
xv6 lab5 lazy

1.eliminate allocation from sbrk()

uint64 sys_sbrk(void) { int addr; int n; if(argint(0, &n) < 0) return -1; addr = myproc()->sz; myproc()->sz += n; //虚拟增加size,而不实际分配物理内存 //if(growproc(n) < 0) //return -1; return addr; }
hart 1 starting init: starting sh $ echo hi usertrap(): unexpected scause 0x000000000000000f pid=3 sepc=0x00000000000012ac stval=0x0000000000004008 panic: uvmunmap: not mapped QEMU: Terminated
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; } //用户态发生异常进入usertrap,对比scause不是系统调用和硬件中断进入这里

讲一下异常链路,shell 调用 sbrk 只改 p->sz,无物理页映射,用户写未映射地址 → 硬件抛出 scause=15 写缺页,usertrap 无缺页处理分支,打印 unexpected scause,调用exit(-1)会销毁当前进程全部资源:关闭文件、释放内存、回收 PCB。调用uvmfree->uvmunmap检测是否有效位等,打印not mapped,执行panic。

2.lazy allocation

要求:在usertrap 添加缺页处理分支,在出错地方映射新的物理内存,然后返回用户态继续执行。

  • 可以通过查看kernel/trap.c中的usertrap()中的r_scause()是13还是15来检查错误是否是页面错误;
  • r_stval()返回stval寄存器的值,该寄存器存放着导致页面错误的虚拟地址;
  • 参考vm.c中的uvmalloc()函数代码,通过调用kalloc()和mappages()来实现本实验;
  • 使用PGROUNDDOWN(va)将错误虚拟地址舍入到页面边界
  • uvmunmap()将崩溃,修改这个函数使得在某些页面未映射的时候不会崩溃
  • 如果内核崩溃,在kernel/kernel.asm中查找sepc
  • 使用pgtbl实验中vmprint函数打印页表的内容
  • 如果产生“incomplete type proc”的错误,请先include “spinklock.h"再"proc.h”

要在usertrap加额外的逻辑处理缺页异常。

syscall(); }else if(r_scause()==13 || r_scause()==15){ //13是读异常,15是写异常 printf("page fault trap: signal %d at address %p\n", r_scause(), r_stval()); uint64 va = PGROUNDDOWN(r_stval());//获取缺页异常的虚拟地址页起始地址 uint64 pa = (uint64)kalloc();//分配物理内存地址 if (pa == 0)//分配失败则杀死进程 { p->killed = 1; }else{ memset((void*)pa, 0, PGSIZE); //将分配的物理内存清零 if (mappages(p->pagetable, va, PGSIZE, pa, PTE_W|PTE_R|PTE_U) != 0) //在页表建立映射,用户态可读可写 { kfree((void*)pa); //映射失败则释放物理内存并杀死进程 p->killed = 1; } } }else if((which_dev = devintr()) != 0){

还得在vm.c的uvmunmap()函数无效位,以及walk获取pte去除,因为sbrk可能size虚拟的较大,而惰性分配只在真正访问某一页时才创建映射。而进程退出时会调用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) continue;//添加 //panic("uvmunmap: walk"); if((*pte & PTE_V) == 0) continue;//添加 //panic("uvmunmap: not mapped"); if(PTE_FLAGS(*pte) == PTE_V) panic("uvmunmap: not a leaf"); if(do_free){ uint64 pa = PTE2PA(*pte); kfree((void*)pa); } *pte = 0; } }

3.Lazytests and Usertests

要求:继续修改代码

  • 修改sbrk()以应对负参数的情况;
  • 如果进程出现页面错误的虚拟地址高于sbrk分配的虚拟内存地址,那么中止该进程
  • 正确处理fork中的父子内存副本;这里应该是实现写时复制的fork;
  • 进程能成功应对当sbrk分配的有效虚拟地址被传递给系统调用但尚未分配该物理地址内存的情况。
  • 处理物理内存不足的情况:当klloc()在页面错误情况下内存分配失败,则终止当前进程
  • 处理页面错误虚拟地址出现在栈下方时候的情况,这个时候的虚拟地址是无效

1.处理sbrk()负数情况

sys_sbrk(void) { int addr; int n; if(argint(0, &n) < 0) return -1; addr = myproc()->sz; if(n > 0){ myproc()->sz += n; //虚拟增加size,而不实际分配物理内存 }else if (myproc()->sz + n < 0) { return -1; }else{ int sz =uvmdealloc(myproc()->pagetable, myproc()->sz, myproc()->sz + n); //uvmdealloc解除映射,返回新的进程size myproc()->sz = sz; } //if(growproc(n) < 0) //return -1; return addr; }

2.usertrap分支再加判断

syscall(); }else if(r_scause()==13 || r_scause()==15){ //13是读异常,15是写异常 //printf("page fault trap: signal %d at address %p\n", r_scause(), r_stval()); uint64 va = PGROUNDDOWN(r_stval());//获取缺页异常的虚拟地址页起始地址 if(va + PGSIZE - 1 <= p->sz && va > PGROUNDDOWN(p->trapframe->sp)){ //保证页面报错的虚拟地址只能在栈上面和p->sz的下面 uint64 pa = (uint64)kalloc();//分配物理内存地址 if (pa == 0)//分配失败则杀死进程 { p->killed = 1; }else{ memset((void*)pa, 0, PGSIZE); //将分配的物理内存清零 if (mappages(p->pagetable, va, PGSIZE, pa, PTE_W|PTE_R|PTE_U) != 0) //在页表建立映射,用户态可读可写 { kfree((void*)pa); //映射失败则释放物理内存并杀死进程 p->killed = 1; } } }else{ p->killed = 1; //不在范围内则杀死进程 } }else if((which_dev = devintr()) != 0){ // ok } 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; }

3.fork()里的uvmcopy,出现wlak获取pte为0及无效位,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;
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/5 13:24:43

Postman+Jenkins接口测试持续集成实战:从零搭建自动化流水线

1. 项目概述&#xff1a;为什么我们需要接口测试的持续集成&#xff1f;在任何一个稍具规模的软件项目中&#xff0c;接口都是系统间通信的基石。无论是微服务架构下的内部调用&#xff0c;还是对外提供的开放API&#xff0c;接口的稳定性和正确性直接决定了整个系统的可用性。…

作者头像 李华
网站建设 2026/7/5 13:24:37

aixingpan.cn API开发文档:api_docs_onechart_common接口指南

aixingpan.cn API开发文档&#xff1a;api_docs_onechart_common接口指南 1. 引言 本文档详细介绍了占星系统的api_docs_onechart_common接口的使用方法&#xff0c;包括请求参数详解、响应数据结构、错误处理机制以及最佳实践建议。 2. 接口基础信息 接口名称: api_docs_onech…

作者头像 李华
网站建设 2026/7/5 13:22:18

Three.js 建筑渐变教程

建筑渐变 Building Gradient ▶ 在线运行案例 案例合集&#xff1a; 三维可视化功能案例&#xff08;threehub.cn&#xff09;开源仓库github地址&#xff1a; https://github.com/z2586300277/three-cesium-examples400个案例代码: 网盘链接 你将学到什么 ShaderMaterial…

作者头像 李华
网站建设 2026/7/5 13:22:12

hsb fpga/ 目录分析

1.主要结构 根据对 fpga/ 目录下代码的深入分析&#xff0c;该目录包含的是 Holoscan Sensor Bridge (HSB) 的 FPGA 核心 IP 及参考设计&#xff0c;其主要功能可以概括为&#xff1a;将各类传感器数据通过 FPGA 采集、封装为网络数据包&#xff0c;并以极低延迟通过以太网&…

作者头像 李华
网站建设 2026/7/5 13:21:00

3分钟上手NSC_BUILDER:Switch游戏文件管理的终极解决方案

3分钟上手NSC_BUILDER&#xff1a;Switch游戏文件管理的终极解决方案 【免费下载链接】NSC_BUILDER Nintendo Switch Cleaner and Builder. A batchfile, python and html script based in hacbuild and Nuts python libraries. Designed initially to erase titlerights encry…

作者头像 李华
网站建设 2026/7/5 13:20:26

高精度电压管理系统设计与STM32实现

1. 高精度电压管理系统的核心价值在工业自动化、精密仪器和嵌入式系统开发中&#xff0c;电压管理精度往往直接决定整个系统的性能上限。传统采用机械电位器或分立元件搭建的电压调节方案&#xff0c;普遍存在三个致命缺陷&#xff1a;调节精度低&#xff08;通常只有1%-5%&…

作者头像 李华