【MTI 6.S081 Lab】Copy-on-write
【MTI 6.S081 Lab】Copy-on-write
- The problem
- The solution
- Implement copy-on-write fork (hard)
- 实验任务
- Hints
- 解决方案
- 问题解决思考
- uvmcopy
- kfree
- kalloc
- kpageref
- cow_handler
- trap
虚拟内存提供了一定程度的间接性:内核可以通过将PTE标记为无效或只读来拦截内存引用,从而导致页面错误,并可以通过修改PTE来更改地址的含义。在计算机系统中有一种说法,任何系统问题都可以通过一定程度的间接性来解决。这个实验室探索了一个例子:copy-on-write fork
The problem
xv6中的fork()系统调用将父进程的所有用户空间内存复制到子进程中。如果父对象很大,则复制可能需要很长时间。更糟糕的是,这项工作经常被大量浪费:fork()通常在子进程中后跟exec(),这会丢弃复制的内存,通常不会使用大部分内存。另一方面,如果父进程和子进程都使用复制的页面,并且其中一个或两个都写入,则确实需要复制。‘
The solution
实现写时复制(COW)fork()的目标是推迟分配和复制物理内存页,直到实际需要副本(如果有的话)。突然安排
COW fork()只为子进程创建一个页表,用户内存的PTE指向父进程的物理页面。COW fork()将父进程和子进程中的所有用户PTE标记为只读。当任一进程尝试写入其中一个COW页面时,CPU将强制执行页面故障。内核页面错误处理程序检测到这种情况,为出错进程分配一页物理内存,将原始页面复制到新页面中,并修改出错进程中的相关PTE以引用新页面,这一次PTE标记为可写。当页面错误处理程序返回时,用户进程将能够写入页面的副本。
COW fork()使得释放实现用户内存的物理页面变得有点棘手。一个给定的物理页面可能被多个进程的页面表引用,并且只有当最后一个引用消失时才应该释放。在像xv6这样的简单内核中,这种记账相当简单,但在生产内核中,这可能很难做到正确;例如,请参阅Patching until the COWs come home.
Implement copy-on-write fork (hard)
实验任务
你的任务是在xv6内核中实现copy-on-write fork。如果修改后的内核成功地执行了cowtest和“usertests-q”程序,那么就完成了。
为了帮助你测试你的实现,我们已经提供了一个xv6程序叫做cowtest。cowtest运行不同的测试,但是在未修改xv6的情况下,第一个测试都会失败。
$ cowtest
simple: fork() failed
”simple“测试分配超过一半可用的物理内存,然后fork()。fork失败的原因是没有足够的物理内存分配给子进程去完整的copy父进程的所有内存。
但你完成这个实验后,你的内核应该能通过所有的cowtest和usertests -q的测试。
$ cowtest
simple: ok
simple: ok
three: zombie!
ok
three: zombie!
ok
three: zombie!
ok
file: ok
ALL COW TESTS PASSED
$ usertests -q
...
ALL TESTS PASSED
这是一个合理的攻击计划。
- 修改uvmcopy()将父进程的物理页面映射到子进程,而不是分配新页面。清除已设置PTE_W的页的子进程和父进程PTE中的PTE_W。
- 修改usertrap()以识别页面错误。当最初可写入的COW页面出现写入页面错误时,使用kalloc()分配一个新页面,将旧页面复制到新页面,然后在PTE_W设置的PTE中安装新页面。最初只读的页面(未映射PTE_W,如文本段中的页面)应保持只读,并在父进程和子进程之间共享;试图写入这样一个页面的进程应该被终止。
- 确保每个物理页在最后一个PTE引用消失时都被释放,而不是之前。实现这一点的一个好方法是,为每个物理页面保留引用该页面的用户页面表数量的“引用计数”。当kalloc()分配页面时,将页面的引用计数设置为1。当fork导致子级共享页面时,增加页面的引用数,每当任何进程将页面从其页面表中删除时,减少页面的计数。只有当引用计数为零时,kfree()才应将页面放回空闲列表。将这些计数保存在一个固定大小的整数数组中是可以的。您必须制定出一个方案,说明如何索引数组以及如何选择其大小。例如,您可以用页面的物理地址除以4096对数组进行索引,并通过kalloc.c中的kinit()为数组指定一个元素数,该元素数等于自由列表中任何页面的最高物理地址。您可以随意修改kalloc.c(例如,kalloc()和kfree())以保持引用计数。
- 当遇到COW页面时,修改copyout()以使用与页面错误相同的方案。
Hints
- 对于每个PTE,有一种方法来记录它是否是COW映射可能是有用的。为此,您可以使用RISC-V PTE中的RSW(保留用于软件)位。
- usertests -q探索了cowtest没有测试的场景,所以不要忘记检查所有测试都通过了。
- 一些有用的宏和页表标志的定义在kernel/rescv.h的末尾。
- 如果发生COW页面故障,并且没有可用内存,则应终止进程。
解决方案
问题解决思考
-
用一位代表是否是COW页面。因为只读页面不存在COW的问题,所以只要对可写页面进行COW映射即可。
第九位代表是否是COW页面,也即
#define PTE_C (1L << 8) -
页表需要创建。也即至少需要三个页表页
-
蹦床页面,每次fork都在allocproc中分配了,所以我们不用管蹦床页面
-
usertrap中确定页面错误的类型

在SCAUSE(注,Supervisor cause寄存器,保存了trap机制中进入到supervisor mode的原因)寄存器的介绍中,有多个与page fault相关的原因。比如,
- 13表示是因为load引起的page fault;
- 15表示是因为store引起的page fault;
- 12表示是因为指令执行引起的page fault。
-
XV6内核会打印出错的虚拟地址,并且这个地址会被保存在STVAL寄存器中,所以要更新SVAL所指虚拟地址中物理页面。
uvmcopy
// Given a parent process's page table, copy
// its memory into a child's page table.
// Copies both the page table and the
// physical memory.
// returns 0 on success, -1 on failure.
// frees any allocated pages on failure.
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");if((*pte & PTE_V) == 0)panic("uvmcopy: page not present");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;// }if ((flags & PTE_C) || (flags & PTE_W)) {flags = (flags & (~PTE_W)) | PTE_C;*pte = (*pte & (~PTE_W)) | PTE_C;}if(mappages(new, i, PGSIZE, (uint64)pa, flags) != 0){goto err;}kpageref(pa, 1);}return 0;err:uvmunmap(new, 0, i / PGSIZE, 1);return -1;
}
kfree
// Free the page of physical memory pointed at by pa,
// which normally should have been returned by a
// call to kalloc(). (The exception is when
// initializing the allocator; see kinit above.)
void
kfree(void *pa)
{struct run *r;if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)panic("kfree");int idx = (uint64)pa / PGSIZE;acquire(&pageref.lock);int count = pageref.count[idx];if (count > 1) { // 还有大于一个在引用,直接返回即可pageref.count[idx]--;release(&pageref.lock);return;}pageref.count[idx] = 0;release(&pageref.lock);// Fill with junk to catch dangling refs.memset(pa, 1, PGSIZE);r = (struct run*)pa;acquire(&kmem.lock);r->next = kmem.freelist;kmem.freelist = r;release(&kmem.lock);
}
kalloc
// Allocate one 4096-byte page of physical memory.
// Returns a pointer that the kernel can use.
// Returns 0 if the memory cannot be allocated.
void *
kalloc(void)
{struct run *r;acquire(&kmem.lock);r = kmem.freelist;if(r) {kmem.freelist = r->next;int idx = (uint64)r / PGSIZE;acquire(&pageref.lock);pageref.count[idx] = 1;release(&pageref.lock);}release(&kmem.lock);if(r)memset((char*)r, 5, PGSIZE); // fill with junkreturn (void*)r;
}
kpageref
void
kpageref(uint64 pa, int inc) {int idx = pa / PGSIZE;acquire(&pageref.lock);pageref.count[idx] += inc;release(&pageref.lock);
}
cow_handler
int
cow_handler(pagetable_t pagetable, uint64 va) {pte_t *pte;uint64 pa;uint flags;char *mem;if (va >= MAXVA) {return -1;}va = PGROUNDDOWN(va);if ((pte = walk(pagetable, va, 0)) == 0) {return 0;}pa = PTE2PA(*pte);flags = PTE_FLAGS(*pte);if (!(flags & PTE_C)) {return -1; // 不是cow}if((mem = kalloc()) == 0)return -1;memmove(mem, (char*)pa, PGSIZE);// 更新flagflags |= PTE_W;flags &= (~PTE_C);*pte = PA2PTE(mem) | flags;kfree((void *)pa); // 减少pa的引用计数return 0;
}
trap
//
// handle an interrupt, exception, or system call from user space.
// called from trampoline.S
//
void
usertrap(void)
{uint64 scause = r_scause(); if(scause == 8){// system call} else if (scause == 15) {if (cow_handler(p->pagetable, r_stval()) < 0) {setkilled(p);}} else if((which_dev = devintr()) != 0){
}
要特别注意死锁,在死锁上花了三四个小时。
相关文章:
【MTI 6.S081 Lab】Copy-on-write
【MTI 6.S081 Lab】Copy-on-write The problemThe solutionImplement copy-on-write fork (hard)实验任务Hints解决方案问题解决思考uvmcopykfreekallockpagerefcow_handlertrap 虚拟内存提供了一定程度的间接性:内核可以通过将PTE标记为无效或只读来拦截内存引用&a…...
【GO】go语言入门实战 —— 命令行在线词典
文章目录 程序介绍抓包代码生成生成request body解析respond body完整代码 字节青训营基础班学习记录。 程序介绍 在运行程序的时候以命令行的形式输入要查询的单词,然后程序返回单词的音标、释义等信息。 示例如下: 抓包 我们选择与网站https://fany…...
模电模电基础知识学习笔记汇总
来源:一周搞(不)定数电模电全集,电子基础知识 11小时 一:模电学习笔记 模电主要讲述:对模拟信号进行产生、放大和处理的模拟集成电路重点知识:常用电子元器件:电阻、电容、电感、保…...
招商银行秋招攻略和考试内容详解
招商银行秋招简介 招商银行是一家股份制商业银行,银行的服务理念已经深入人心,在社会竞争愈来愈烈的今天,招商银行的招牌无疑是个香饽饽,很多人也慕名而至,纷纷向招商银行投出了简历。那么秋招银行的秋招开始时间是多…...
【Linux】四、开发工具
一、vim 编辑器(只能写代码) 1、只关注如何写代码,不会关注代码的正确性; 2、一般写代码在Windows环境下写,而vim是Linux下相对来说功能最强的编辑器; 二、vim的操作 vim ---打开vim shift键 加 ࿱…...
前后端分离实现博客系统
文章目录 博客系统前言1. 前端1.1 登陆页面1.2 博客列表页面1.3 博客详情页面1.4 博客编辑页面 2. 后端2.1 项目部署2.1.1 创建maven项目2.1.2 引入依赖2.1.3 创建目录结构2.1.4 部署程序 2.2 逻辑设计2.2.1 数据库设计2.2.2 实体类设计2.2.3 Dao层设计2.2.3.1 BlogDao 2.2.4 D…...
面试题-TS(六):TypeScript 中的泛型是什么?
面试题-TS(6):TypeScript 中的泛型是什么? 在TypeScript中,泛型(Generics)是一种强大的特性,它允许我们在编写可重用的代码时增加灵活性。泛型使得我们可以编写不特定数据类型的代码,从而提高代…...
QT DAY4
1.思维导图 2.手动完成服务器的实现,并具体程序要注释清楚 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer> #include <QTcpSocket> #include <QMessageBox> #include <QList> #include <QD…...
最新Ai创作源码ChatGPT商用运营源码/支持GPT4.0+支持ai绘画+支持Mind思维导图生成
本系统使用Nestjs和Vue3框架技术,持续集成AI能力到本系统! 支持GPT3模型、GPT4模型Midjourney专业绘画(全自定义调参)、Midjourney以图生图、Dall-E2绘画Mind思维导图生成应用工作台(Prompt)AI绘画广场自定…...
一个go的支持多语言的error自动生成插件
大家好,我是peachesTao,今天给大家推荐一个go的支持多语言的error自动生成的插件,插件主页可以访问下方链接。 在一个多语言国际化的项目中,后端接口返回给前端的错误描述也需要国际化,我们来看一下后端给前端返回多语…...
wireshark抓包新手使用教程(超详细)
一、简介 Wireshark是一款非常流行的网络封包分析软件,可以截取各种网络数据包,并显示数据包详细信息。 为了安全考虑,wireshark只能查看封包,而不能修改封包的内容,或者发送封包。 wireshark能获取HTTP,也…...
平均列顺序对列排斥能的影响
( A, B )---3*30*2---( 1, 0 )( 0, 1 ) 让网络的输入只有3个节点,AB训练集各由5张二值化的图片组成,让A有6个1,B有4个1,并且让这10个1的位置没有重合。比较迭代次数的顺序。 其中有9组数据 差值结构 A-B 迭代次数 构造平均列 …...
微信小程序-处理ios无法播放语音的问题
背景 框架:tarovue3 问题:今天搞小程序语音播放功能,开放工具播放正常,但是到ios手机上调试时无法播放,在网上找到个好办法 解决方案 核心代码 Taro.setInnerAudioOption({obeyMuteSwitch: false // 解决有一些IOS无…...
区块链 2.0笔记
区块链 2.0 以太坊概述 相对于比特币的几点改进 缩短出块时间至10多秒ghost共识机制mining puzzle BTC:计算密集型ETH:memory-hard(限制ASIC) proof of work->proof of stake对智能合约的支持 BTC:decentralized currencyETH:decentral…...
深入理解Vue响应式系统:数据绑定探索
🌷🍁 博主猫头虎 带您 Go to New World.✨🍁 🦄 博客首页——猫头虎的博客🎐 🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 &a…...
web流程自动化详解
今天给大家带来Selenium的相关解释操作 一、Selenium Selenium是一个用于自动化Web浏览器操作的开源工具和框架。它提供了一组API(应用程序接口),可以让开发人员使用多种编程语言(如Java、Python、C#等)编写测试脚本&…...
什么是框架?为什么要学框架?
一、什么是框架 框架是整个或部分应用的可重用设计,是可定制化的应用骨架。它可以帮开发人员简化开发过程,提高开发效率。 项目里有一部分代码:和业务无关,而又不得不写的代码>框架 项目里剩下的部分代码:实现业务…...
什么是 Sass?
Sass 介绍 什么是 Sass? 官方标语 世界上最成熟、最稳定、最强大的专业级 CSS 扩展语言。怎么理解这句话呢?我们平时写的 CSS 代码可以理解为静态样式语言,而 Scss 就是动态样式语言,何为动态?就是让你写 CSS 跟写 …...
Kotlin~Memento备忘录模式
概念 备忘录模式是一种行为型设计模式,用于捕获和存储对象的内部状态,并在需要时将对象恢复到之前的状态。 备忘录模式允许在不暴露对象内部实现细节的情况下,对对象进行状态的保存和恢复。 角色介绍 Originator:原发器&#x…...
单链表的多语言表达:C++、Java、Python、Go、Rust
单链表 是一种链式数据结构,由一个头节点和一些指向下一个节点的指针组成。每个节点包含一个数据元素和指向下一个节点的指针。头节点没有数据,只用于表示链表的开始位置。 单链表的主要操作包括: 添加元素:在链表的头部添加新…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...
深入理解Optional:处理空指针异常
1. 使用Optional处理可能为空的集合 在Java开发中,集合判空是一个常见但容易出错的场景。传统方式虽然可行,但存在一些潜在问题: // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...
C# winform教程(二)----checkbox
一、作用 提供一个用户选择或者不选的状态,这是一个可以多选的控件。 二、属性 其实功能大差不差,除了特殊的几个外,与button基本相同,所有说几个独有的 checkbox属性 名称内容含义appearance控件外观可以变成按钮形状checkali…...
边缘计算网关提升水产养殖尾水处理的远程运维效率
一、项目背景 随着水产养殖行业的快速发展,养殖尾水的处理成为了一个亟待解决的环保问题。传统的尾水处理方式不仅效率低下,而且难以实现精准监控和管理。为了提升尾水处理的效果和效率,同时降低人力成本,某大型水产养殖企业决定…...
AWSLambda之设置时区
目标 希望Lambda运行的时区是东八区。 解决 只需要设置lambda的环境变量TZ为东八区时区即可,即Asia/Shanghai。 参考 使用 Lambda 环境变量...
