当前位置: 首页 > news >正文

【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

这是一个合理的攻击计划。

  1. 修改uvmcopy()将父进程的物理页面映射到子进程,而不是分配新页面。清除已设置PTE_W的页的子进程和父进程PTE中的PTE_W。
  2. 修改usertrap()以识别页面错误。当最初可写入的COW页面出现写入页面错误时,使用kalloc()分配一个新页面,将旧页面复制到新页面,然后在PTE_W设置的PTE中安装新页面。最初只读的页面(未映射PTE_W,如文本段中的页面)应保持只读,并在父进程和子进程之间共享;试图写入这样一个页面的进程应该被终止。
  3. 确保每个物理页在最后一个PTE引用消失时都被释放,而不是之前。实现这一点的一个好方法是,为每个物理页面保留引用该页面的用户页面表数量的“引用计数”。当kalloc()分配页面时,将页面的引用计数设置为1。当fork导致子级共享页面时,增加页面的引用数,每当任何进程将页面从其页面表中删除时,减少页面的计数。只有当引用计数为零时,kfree()才应将页面放回空闲列表。将这些计数保存在一个固定大小的整数数组中是可以的。您必须制定出一个方案,说明如何索引数组以及如何选择其大小。例如,您可以用页面的物理地址除以4096对数组进行索引,并通过kalloc.c中的kinit()为数组指定一个元素数,该元素数等于自由列表中任何页面的最高物理地址。您可以随意修改kalloc.c(例如,kalloc()和kfree())以保持引用计数。
  4. 当遇到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 虚拟内存提供了一定程度的间接性&#xff1a;内核可以通过将PTE标记为无效或只读来拦截内存引用&a…...

【GO】go语言入门实战 —— 命令行在线词典

文章目录 程序介绍抓包代码生成生成request body解析respond body完整代码 字节青训营基础班学习记录。 程序介绍 在运行程序的时候以命令行的形式输入要查询的单词&#xff0c;然后程序返回单词的音标、释义等信息。 示例如下&#xff1a; 抓包 我们选择与网站https://fany…...

模电模电基础知识学习笔记汇总

来源&#xff1a;一周搞&#xff08;不&#xff09;定数电模电全集&#xff0c;电子基础知识 11小时 一&#xff1a;模电学习笔记 模电主要讲述&#xff1a;对模拟信号进行产生、放大和处理的模拟集成电路重点知识&#xff1a;常用电子元器件&#xff1a;电阻、电容、电感、保…...

招商银行秋招攻略和考试内容详解

招商银行秋招简介 招商银行是一家股份制商业银行&#xff0c;银行的服务理念已经深入人心&#xff0c;在社会竞争愈来愈烈的今天&#xff0c;招商银行的招牌无疑是个香饽饽&#xff0c;很多人也慕名而至&#xff0c;纷纷向招商银行投出了简历。那么秋招银行的秋招开始时间是多…...

【Linux】四、开发工具

一、vim 编辑器&#xff08;只能写代码&#xff09; 1、只关注如何写代码&#xff0c;不会关注代码的正确性&#xff1b; 2、一般写代码在Windows环境下写&#xff0c;而vim是Linux下相对来说功能最强的编辑器&#xff1b; 二、vim的操作 vim ---打开vim shift键 加 &#xff1…...

前后端分离实现博客系统

文章目录 博客系统前言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)&#xff1a;TypeScript 中的泛型是什么&#xff1f; 在TypeScript中&#xff0c;泛型&#xff08;Generics&#xff09;是一种强大的特性&#xff0c;它允许我们在编写可重用的代码时增加灵活性。泛型使得我们可以编写不特定数据类型的代码&#xff0c;从而提高代…...

QT DAY4

1.思维导图 2.手动完成服务器的实现&#xff0c;并具体程序要注释清楚 头文件 #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框架技术&#xff0c;持续集成AI能力到本系统&#xff01; 支持GPT3模型、GPT4模型Midjourney专业绘画&#xff08;全自定义调参&#xff09;、Midjourney以图生图、Dall-E2绘画Mind思维导图生成应用工作台&#xff08;Prompt&#xff09;AI绘画广场自定…...

一个go的支持多语言的error自动生成插件

大家好&#xff0c;我是peachesTao&#xff0c;今天给大家推荐一个go的支持多语言的error自动生成的插件&#xff0c;插件主页可以访问下方链接。 在一个多语言国际化的项目中&#xff0c;后端接口返回给前端的错误描述也需要国际化&#xff0c;我们来看一下后端给前端返回多语…...

wireshark抓包新手使用教程(超详细)

一、简介 Wireshark是一款非常流行的网络封包分析软件&#xff0c;可以截取各种网络数据包&#xff0c;并显示数据包详细信息。 为了安全考虑&#xff0c;wireshark只能查看封包&#xff0c;而不能修改封包的内容&#xff0c;或者发送封包。 wireshark能获取HTTP&#xff0c;也…...

平均列顺序对列排斥能的影响

( A, B )---3*30*2---( 1, 0 )( 0, 1 ) 让网络的输入只有3个节点&#xff0c;AB训练集各由5张二值化的图片组成&#xff0c;让A有6个1&#xff0c;B有4个1&#xff0c;并且让这10个1的位置没有重合。比较迭代次数的顺序。 其中有9组数据 差值结构 A-B 迭代次数 构造平均列 …...

微信小程序-处理ios无法播放语音的问题

背景 框架&#xff1a;tarovue3 问题&#xff1a;今天搞小程序语音播放功能&#xff0c;开放工具播放正常&#xff0c;但是到ios手机上调试时无法播放&#xff0c;在网上找到个好办法 解决方案 核心代码 Taro.setInnerAudioOption({obeyMuteSwitch: false // 解决有一些IOS无…...

区块链 2.0笔记

区块链 2.0 以太坊概述 相对于比特币的几点改进 缩短出块时间至10多秒ghost共识机制mining puzzle BTC:计算密集型ETH&#xff1a;memory-hard(限制ASIC) proof of work->proof of stake对智能合约的支持 BTC&#xff1a;decentralized currencyETH&#xff1a;decentral…...

深入理解Vue响应式系统:数据绑定探索

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…...

web流程自动化详解

今天给大家带来Selenium的相关解释操作 一、Selenium Selenium是一个用于自动化Web浏览器操作的开源工具和框架。它提供了一组API&#xff08;应用程序接口&#xff09;&#xff0c;可以让开发人员使用多种编程语言&#xff08;如Java、Python、C#等&#xff09;编写测试脚本&…...

什么是框架?为什么要学框架?

一、什么是框架 框架是整个或部分应用的可重用设计&#xff0c;是可定制化的应用骨架。它可以帮开发人员简化开发过程&#xff0c;提高开发效率。 项目里有一部分代码&#xff1a;和业务无关&#xff0c;而又不得不写的代码>框架 项目里剩下的部分代码&#xff1a;实现业务…...

什么是 Sass?

Sass 介绍 什么是 Sass&#xff1f; 官方标语 世界上最成熟、最稳定、最强大的专业级 CSS 扩展语言。怎么理解这句话呢&#xff1f;我们平时写的 CSS 代码可以理解为静态样式语言&#xff0c;而 Scss 就是动态样式语言&#xff0c;何为动态&#xff1f;就是让你写 CSS 跟写 …...

Kotlin~Memento备忘录模式

概念 备忘录模式是一种行为型设计模式&#xff0c;用于捕获和存储对象的内部状态&#xff0c;并在需要时将对象恢复到之前的状态。 备忘录模式允许在不暴露对象内部实现细节的情况下&#xff0c;对对象进行状态的保存和恢复。 角色介绍 Originator&#xff1a;原发器&#x…...

单链表的多语言表达:C++、Java、Python、Go、Rust

单链表 是一种链式数据结构&#xff0c;由一个头节点和一些指向下一个节点的指针组成。每个节点包含一个数据元素和指向下一个节点的指针。头节点没有数据&#xff0c;只用于表示链表的开始位置。 单链表的主要操作包括&#xff1a; 添加元素&#xff1a;在链表的头部添加新…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)

一、数据处理与分析实战 &#xff08;一&#xff09;实时滤波与参数调整 基础滤波操作 60Hz 工频滤波&#xff1a;勾选界面右侧 “60Hz” 复选框&#xff0c;可有效抑制电网干扰&#xff08;适用于北美地区&#xff0c;欧洲用户可调整为 50Hz&#xff09;。 平滑处理&…...

HTML 列表、表格、表单

1 列表标签 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表。 例如&#xff1a; 1.1 无序列表 标签&#xff1a;ul 嵌套 li&#xff0c;ul是无序列表&#xff0c;li是列表条目。 注意事项&#xff1a; ul 标签里面只能包裹 li…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业

6月9日&#xff0c;国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解&#xff0c;“超级…...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

MySQL 主从同步异常处理

阅读原文&#xff1a;https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主&#xff0c;遇到的这个错误&#xff1a; Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一&#xff0c;通常表示&#xff…...

ubuntu22.04有线网络无法连接,图标也没了

今天突然无法有线网络无法连接任何设备&#xff0c;并且图标都没了 错误案例 往上一顿搜索&#xff0c;试了很多博客都不行&#xff0c;比如 Ubuntu22.04右上角网络图标消失 最后解决的办法 下载网卡驱动&#xff0c;重新安装 操作步骤 查看自己网卡的型号 lspci | gre…...

Android写一个捕获全局异常的工具类

项目开发和实际运行过程中难免会遇到异常发生&#xff0c;系统提供了一个可以捕获全局异常的工具Uncaughtexceptionhandler&#xff0c;它是Thread的子类&#xff08;就是package java.lang;里线程的Thread&#xff09;。本文将利用它将设备信息、报错信息以及错误的发生时间都…...

第八部分:阶段项目 6:构建 React 前端应用

现在&#xff0c;是时候将你学到的 React 基础知识付诸实践&#xff0c;构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段&#xff0c;你可以先使用模拟数据&#xff0c;或者如果你的后端 API&#xff08;阶段项目 5&#xff09;已经搭建好&#xff0c;可以直接连…...

从实验室到产业:IndexTTS 在六大核心场景的落地实践

一、内容创作&#xff1a;重构数字内容生产范式 在短视频创作领域&#xff0c;IndexTTS 的语音克隆技术彻底改变了配音流程。B 站 UP 主通过 5 秒参考音频即可克隆出郭老师音色&#xff0c;生成的 “各位吴彦祖们大家好” 语音相似度达 97%&#xff0c;单条视频播放量突破百万…...