MIT 6.S081 xv6 lazy page allocation 实验篇(lab5):Lazy allocation (moderate)
Lazy allocation (moderate)实验目标这是 Lab5Lazy page allocation的第二步也是真正让惰性分配落地的核心关。上一步Eliminate allocation from sbrk我们删掉了sbrk里的实际分配只记账、不建映射结果访问新地址就panic。本步的目标在缺页发生时由内核当场补上这块内存。练习目标在usertrap()里识别用户态缺页异常scause 13或15为其现场分配一页物理内存并建好映射让进程从缺页指令处无缝继续。修改uvmunmap()让它在解除映射时遇到未映射的页不再 panic而是直接跳过否则上一步埋下的账记了、页没给在进程退出时仍会炸。最终让echo hi这类简单命令跑通理解 lazy allocation 的完整闭环申请时只记账 → 访问时缺页 → 内核补分配 → 继续运行。官方 lab 原文把这一步描述为Modify the code intrap.cto respond to a page fault from user space by mapping a newly-allocated page of physical memory at the faulting address, and then returning back to user space to let the process continue executing. You should add your code just before theprintfcall that produced the “usertrap(): …” message.前置知识1. 缺页异常落在usertrap承接上一步进程访问sbrk新记大的、却没映射的地址 → MMU 发现 PTE 无效 → CPU 触发页错误异常经uservec陷入内核、进入usertrap()。所以在usertrap里插桩正是处理它的正确位置。scause编码xv6kernel/riscv.hscause含义8(0x8)系统调用ecall13(0xd)读访问缺页load page fault15(0xf)写访问缺页store/AMO page faultr_stval()返回触发缺页的虚拟地址r_sepc()返回缺页指令地址处理完从这里重执行。2. lazy handler 的三板斧为缺页地址补一页本质是照抄uvmalloc()的核心逻辑官方 hint 就是Steal code from uvmalloc()kalloc()从空闲链表抠出一页物理内存。memset(pa, 0, PGSIZE)必须清零。xv6 的物理页可能此前装过内核/其他进程数据不清零会把残留内容甚至内核机密暴露给用户进程——这是安全与正确性的硬要求。mappages()在页表里把缺页虚拟地址页对齐↔ 这块物理页建好映射并赋予正确权限。3. PTE 权限位kernel/riscv.h里定义的页表项标志位位含义PTE_VValid映射有效PTE_R可读PTE_W可写PTE_X可执行PTE_U用户态可访问U-mode 能访问用户进程的堆页必须带PTE_U才能被用户态访问R|W是读写所需。4. 为什么必须改uvmunmap上一步我们讲透过panic发生在进程退出、内核uvmunmap(pagetable, 0, sz, 1)解映射时——它走到sbrk记大却从未建映射的那段发现 PTE 无效就panic(uvmunmap: not mapped)。现在惰性分配下[0, sz)里有空洞未分配页是常态而非错误。所以要让uvmunmap遇到无效 PTE 时跳过而非 panic——这是本步必须的配套改动。5. 本步改动文件文件改动kernel/trap.cusertrap()新增缺页分支lazy handlerkernel/vm.cuvmunmap()遇无效 PTE 改continue跳过实现思路整体闭环进程访问未映射地址 ──(MMU 缺页)── usertrap(scause13/15) │ ├─ 取 fault_va r_stval() ├─ 校验合法性fault_va 在栈顶之上、sz 之下属于合法堆区 ├─ kalloc() 拿一页物理内存 ├─ memset(pa, 0, PGSIZE) 清零 ├─ mappages() 建映射R|W|X|U └─ usertrapret() 返回用户态从缺页指令处重新执行 ── 这次访问成功关键设计点插入位置在usertrap原有if(scause8)系统调用、else if(devintr)设备中断之后原elseunexpected之前新增else if(r_scause()13 || r_scause()15)分支。这样缺页会被优先识别、不再落进unexpected兜底。合法性判断PGROUNDUP(p-trapframe-sp) - 1 fault_va fault_va p-sz。含义缺页地址必须高于用户栈顶sp之上才是堆区、且低于进程大小sz在sbrk记账的范围内。这一步把合法惰性分配请求和真正非法的访问区分开。分配 清零 映射三板斧一气呵成mappages失败极少见如页表满则释放刚拿到的物理页并标记进程killed。uvmunmap配套两处panic改为continue让解映射对空洞鲁棒。注这一步是官方定义的moderate阶段目标是让echo hi工作。判断条件用的是栈顶之上、sz 之下的简化版更严格的边界越界kill、栈下方 Guard 页、fork 复制、系统调用传参、OOM在后续lazytests阶段补齐见复盘·前瞻。代码实现kernel/trap.c——usertrap新增缺页分支/* * kernel/trap.c */voidusertrap(void){intwhich_dev0;if((r_sstatus()SSTATUS_SPP)!0)panic(usertrap: not from user mode);// send interrupts and exceptions to kerneltrap(),// since were now in the kernel.w_stvec((uint64)kernelvec);structproc*pmyproc();// save user program counter.p-trapframe-epcr_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-epc4;// an interrupt will change sstatus c registers,// so dont enable until done with those registers.intr_on();syscall();}elseif((which_devdevintr())!0){// ok}elseif((r_scause()13||r_scause()15)){// 惰性分配导致的缺页异常uint64 fault_var_stval();// 获取引发缺页异常的虚拟地址char*pa0;// 虚拟地址对应的物理地址(待映射)// 判断虚拟地址 fault_va 是否在进程栈空间之中if(PGROUNDUP(p-trapframe-sp)-1fault_vafault_vap-sz){// 分配一页物理内存if((pakalloc())0)panic(usertrap: kalloc\n);memset(pa,0,PGSIZE);// 将 fault_va 映射到 paif(mappages(p-pagetable,PGROUNDDOWN(fault_va),PGSIZE,(uint64)pa,PTE_R|PTE_W|PTE_X|PTE_U)!0){printf(lazy alloc: failed to map page\n);kfree(pa);p-killed1;}}}else{printf(usertrap(): unexpected scause %p pid%d\n,r_scause(),p-pid);printf( sepc%p stval%p\n,r_sepc(),r_stval());p-killed1;}if(p-killed)exit(-1);// give up the CPU if this is a timer interrupt.if(which_dev2)yield();usertrapret();}逐行理解else if ((r_scause() 13 || r_scause() 15))识别读/写缺页lazy 触发的页面错误。fault_va r_stval();拿到触发缺页的虚拟地址。PGROUNDUP(p-trapframe-sp) - 1 fault_va fault_va p-sz合法性闸门——只在栈顶之上、进程大小sz之下的堆区才补分配。kalloc()拿物理页memset(pa, 0, PGSIZE)清零安全必需mappages(..., PTE_R|PTE_W|PTE_X|PTE_U)建映射R/W/X/U 全给教学代码图省事。mappages失败则kfree(pa)回收物理页并p-killed 1。注意成功时什么也没改、也没 return——usertrap会自然走到末尾的usertrapret()带着新映射返回用户态CPU 重新执行那条缺页指令这次地址已映射访问成功。kernel/vm.c——uvmunmap不再为缺页 panic/* * kernel/vm.c */voiduvmunmap(pagetable_tpagetable,uint64 va,uint64 npages,intdo_free){uint64 a;pte_t*pte;if((va%PGSIZE)!0)panic(uvmunmap: not aligned);for(ava;avanpages*PGSIZE;aPGSIZE){if((ptewalk(pagetable,a,0))0)// panic(uvmunmap: walk);continue;// 惰性分配遇到不存在的 pte 就跳过if((*ptePTE_V)0)// panic(uvmunmap: not mapped);continue;// 同上if(PTE_FLAGS(*pte)PTE_V)panic(uvmunmap: not a leaf);if(do_free){uint64 paPTE2PA(*pte);kfree((void*)pa);}*pte0;}}改动极小但关键walk()返回 0页表中间层都还没建或PTE_V 0叶子 PTE 无效→ 原代码panic现改为continue跳过。这样惰性分配留下的空洞在进程退出/exec 解映射时安静跳过上一步的panic: uvmunmap: not mapped彻底消失。其余逻辑释放物理页、*pte 0不变——确实映射过的页照常回收。验证按官方步骤启动 xv6执行最简单命令$makeqemu$echohi hi应正常打印hi而不再 panic。这说明shell 经sbrk申请的堆只记账在被访问时由usertrap的 lazy handler 当场补了一页物理内存并映射进程得以继续。为确认确实发生了惰性分配可借助 Lab3pgtbl的vmprint若已在该分支或临时printf缺页分支观察echo hi期间至少触发了一次甚至两次缺页——这正是 lazy 的实证。提示本步只要求echo hi工作。更严苛的lazytests/usertests还会测越界、fork、系统调用传参等边界当前实现在这些场景可能仍不过——它们是下一个阶段要补的见复盘·前瞻。复盘本实验解决了什么lazy allocation 真正闭环了sbrk记账第一步→ 访问缺页CPU→usertrap补分配本步→ 重执行成功。三步走完内存从申请即分配变成了用到才给。亲手实现了一次缺页处理把 Lab4 学的异常 →usertrap框架第一次用于真正修复一个问题而非打印/报警。scause/stval/sepc三个寄存器从概念变成了手里的工具。uvmunmap的鲁棒化理解了解映射遇到空洞是惰性分配的固有常态配套改成跳过——这一步让整个机制自洽。与真实操作系统Linux的对比demand paging 是现代 OS 的标配本步就是它的极简版Linux 的堆brk/mmap、文件映射、共享库默认都是访问时才分配物理页minor fault若还需从磁盘读则是 major fault。Linux 的缺页处理在do_page_fault()里同样要区分合法匿名页/文件页/非法访问非法访问会发SIGSEGV杀进程——对应我们下一步要加的越界kill。xv6 这里kalloc失败直接panic而 Linux 会走 OOM killer 选进程牺牲——复杂度差异巨大但骨架一致。收获demand paging / lazy allocation能讲清为什么不在申请时分配快、省、支持稀疏/超额是 OS 内存管理章节高频题。page fault 全流程scause13/15 区分读写缺页、stval给地址、sepc用于重执行缺页不是错误而是正常的中断信号handler 补完映射后从原指令重试。kalloc后必须memset 0信息安全视角的硬要求不清零会泄露内核/其他进程数据也是面试加分点。uvmunmap的空洞容忍理解映射空间与实际分配解耦后所有解映射/回收路径都必须对空洞鲁棒。前瞻lazytests 阶段还要补的边界当前实现能跑echo hi但官方lazytests/usertests会测更多场景需要继续加固越界要kill若fault_va p-sz超过sbrk分配的上界应p-killed 1而非默默分配——否则会无限缺页死循环注意当前else if已匹配scause13/15越界地址不会落进原else会静默无操作 → 回用户态又缺页 → busy loop。栈下方 Guard 页fault_va落在栈正下方那页非法也要kill。fork 复制uvmcopy要能处理父进程有惰性空洞的情况空洞子进程也记为空洞不能试图复制不存在的页。系统调用传参read/write等传入sbrk地址但内存未分配时内核copyin/copyout也要能触发分配。OOMkalloc()失败时不应panic整个内核而应kill当前进程用p-killed 1。建议把前置知识里的 PTE 权限表和实现思路里的 lazy 闭环图存下——Lab6 的 Copy-on-Write fork 会复用同一套缺页处理框架只不过 handler 从分配新页变成复制父页思维模型一脉相承。

相关新闻

四层防御解决LLM输出JSON解析难题:从提示词到后处理的工程实践

四层防御解决LLM输出JSON解析难题:从提示词到后处理的工程实践

在调用大语言模型(LLM)生成结构化数据时,你是否也遇到过这样的场景:你明确要求模型输出一个干净的 JSON 对象,但得到的回复却总是夹杂着“好的,这是您要的 JSON:”这样的前言,或者“…

2026/10/12 23:06:44 阅读更多 →
XHTTP 和 HTTP 到底有什么区别?从协议原理到 VLESS + XHTTP + REALITY 一次讲透

XHTTP 和 HTTP 到底有什么区别?从协议原理到 VLESS + XHTTP + REALITY 一次讲透

一、先给结论:HTTP 和 XHTTP 不是一个层级把整个链路拆开,会清楚很多。 普通 Web 访问通常可以抽象为: Browser / App↓ HTTP/1.1 / HTTP/2 / HTTP/3↓ TLS↓ TCP / QUIC↓ IP而 Xray 中常见的: VLESS XHTTP REALITY更准确的分层…

2026/10/12 23:05:48 阅读更多 →
企业媒体发稿怎么做好AI收录沉淀?传播易智能投放解决方案

企业媒体发稿怎么做好AI收录沉淀?传播易智能投放解决方案

进入2026年,企业新闻发稿的行业核心痛点已经完成迭代。当下品牌传播的核心难题,早已不是找不到发稿渠道、无法完成稿件发布,而是普遍面临稿件投放去向不明、受众触达模糊、搜索收录留存不稳定、传播效果无法核验溯源等一系列行业顽疾。传统中…

2026/10/12 23:05:51 阅读更多 →

最新新闻

Ubuntu下ORB-SLAM2编译安装全攻略:依赖解析与避坑指南

Ubuntu下ORB-SLAM2编译安装全攻略:依赖解析与避坑指南

1. 为什么 ORB-SLAM2 的编译安装值得单独写一篇ORB-SLAM2 是视觉 SLAM 领域里被引用最多、被复现最多的开源系统之一,支持单目、双目和 RGB-D 三种传感器模式,能做实时定位与稀疏建图。很多人第一次接触它,都是被论文里的轨迹图和回环效果吸引…

2026/10/13 7:56:22 阅读更多 →
Windows批量部署实战:从PXE到MDT的标准化系统交付

Windows批量部署实战:从PXE到MDT的标准化系统交付

1. 这不是重装系统,是批量部署——为什么“批量安装Windows系统”正在成为一线运维的硬通货你有没有遇到过这样的场景:刚接手一个新机房,20台全新裸机整齐排开,每台都要手动插U盘、选分区、输密钥、装驱动、打补丁、配策略……光是…

2026/10/13 7:56:22 阅读更多 →
WSL与GitHub Copilot组合实战:从环境搭建到效率翻倍

WSL与GitHub Copilot组合实战:从环境搭建到效率翻倍

1. 环境准备:把WSL这套地基彻底打牢 1.1 为什么我最终选择WSL而不是双系统或虚拟机 这两年我一直在Windows上做后端和前端开发,一直在折腾环境。双系统切来切去太费时间,虚拟机虽然隔离性好但资源开销大,硬盘空间和内存都被吃掉一…

2026/10/13 7:56:22 阅读更多 →
基于梯度缺陷ANCF梁单元的单悬臂梁重力弯曲仿真全解析

基于梯度缺陷ANCF梁单元的单悬臂梁重力弯曲仿真全解析

在结构力学与计算仿真这个圈子里,悬臂梁是一个绕不开的经典基准模型,但最近我做的这个项目——基于梯度缺陷ANCF梁单元的单悬臂梁重力弯曲仿真,可以说是在经典基础上叠了一层很有意思的buff。它不仅是把一根梁一端固定、让它自己在重力下垂下…

2026/10/13 7:56:22 阅读更多 →
Windows Server上搭建PostgreSQL测试库:安装配置与备份恢复实战指南

Windows Server上搭建PostgreSQL测试库:安装配置与备份恢复实战指南

做数据库测试环境搭建,听起来好像就是装个数据库、建几个库的问题,实际上真到落地的时候,版本选择、编码格式、账号权限、远程访问、备份恢复,哪一环松了都会在工作日晚上还回来。前一阵我们小组接到一个内部订单系统的测试环境需…

2026/10/13 7:56:22 阅读更多 →
SQL UNIQUE约束详解:原理、数据库差异与防重复设计实践

SQL UNIQUE约束详解:原理、数据库差异与防重复设计实践

1. UNIQUE约束到底是什么,为什么你需要它做数据库设计这些年,我见过太多因为“忘记加唯一约束”而崩溃的业务系统。最典型的场景:用户注册接口被脚本刷了一遍,username重复了;订单表里同一笔订单被回调程序insert了两次…

2026/10/13 7:55:19 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →