xv6实验课程--xv6的写时复制fork(2023)
7. xv6实验课程--xv6的写时拷贝(COW)(2021)
7. xv6实验课程--xv6懒惰分页分配(lazy)(2020)
本文来源:
https://mp.weixin.qq.com/s/XJkhjrlP232ZDsRyXd0oHQ
已完成的实验代码可以从下列网站获取:
git clone https://gitee.com/lhwhit1966/xv6-labs-2023.git
注:在2020年秋季课程中有一个懒惰分页分配的实验(Lab: xv6 lazy page allocation),在2021年秋季课程中没有这个实验,想了解该实验的读者可以阅读本公众号中的文章:xv6实验课程--xv6懒惰分配
虚拟内存提供了一个间接层(a level of indirection):内核可以通过将PTE标记为无效或只读来拦截内存引用,从而导致页面错误,并可以通过修改PTE来更改地址的含义。在计算机系统中有一种说法,任何系统问题都可以使用间接层来解决。本实验探索了一个例子:写时复制fork。
开始实验,请切换到cow分支:
$git fetch
$git checkout cow
$make clean
建议按以下方法操作。
删除xv6-labs-2023目录,然后
$ git clone git://g.csail.mit.edu/xv6-labs-2023
$ cd xv6-labs-2023
$ git checkout cow
注:写时复制(copy-on-write,COW)这个想法至少可以回溯到TENEX操作系统[1],它很简单:如果操作系统需要将一个页面从一个地址空间复制到另一个地址空间,不是实际复制它,而是将其映射到目标地址空间,并在两个地址空间中将其标记为只读。如果两个地址空间都只读页面,则不会采取进一步的操作,因此操作系统已经实现了快速复制而不实际移动任何数据。但是,如果其中一个地址空间确实尝试写入页面,就会陷入操作系统。操作系统会注意到该页面是一个COW 页面,因此(懒惰地)分配一个新页,用数据填充该页,并将该新页映射到出错进程的地址空间。该过程随后继续,现在有了自己的页面私有副本。
COW之所以有用是有很多原因的。当然,任何类型的共享库都可以在写入时复制映射到许多进程的地址空间,从而节省宝贵的内存空间。在UNIX系统中,由于fork()和exec()的语义,COW 更加关键。fork()会创建调用者地址空间的精确副本。对于大的地址空间,这样的复制过程很慢,并且是数据密集的。更糟糕的是,大部分地址空间会被随后的exec()调用立即覆盖,它用即将执行的程序覆盖调用进程的地址空间。通过改为执行写操作时才进行复制的fork(),操作系统避免了大量不必要的复制,这不但保留了正确的语义,还提高了系统的性能。
问题
xv6中的fork()系统调用将父进程的所有用户空间存储的内容复制到子进程中。如果父进程的用户空间很大,复制可能需要很长时间。更糟糕的是,这项工作的大部分往往被浪费了,例如,fork()后的子进程紧接着执行exec()函数,这将导致子进程丢弃复制的内容,也就是说,这些复制的内容根本就没有被用到。另一方面,当父进程和子进程使用同一个页面时,若其中的一个或两个进程要对页面进行写操作,此时确实需要一个副本。
解决方案
本实验的目标是推迟由fork()创建的子进程分配和复制物理内存页,直到实际需要时才复制(如果需要的话)。
COW fork()只为子进程创建一个页表,用户内存的PTE指向父进程的物理页。COW fork()将父子进程中的所有用户PTE标记为不可写。当父子进程中的任一个试图写入其中一个COW页时,将强制CPU执行页错误。内核页错误处理程序检测到这种情况,为出错进程分配一页物理内存,将原始页复制到新页中,并修改出错进程中的相关PTE以引用新页,并标记父子进程相应的PTE对应的页为可写。当页面错误处理程序返回时,用户进程将能够写入其页面副本。
COW fork()使得释放用户内存的物理页变得有点复杂。给定的物理页可以由多个进程的页表引用,并且只有在最后一个引用去除时才能释放。在像xv6这样的简单内核中,其日志相当简单,但是在实际产品的内核中,做到正确通常是很难的,参看文章Patching until the COWs come home[7]。
实验:实现写时复制(难度:困难)
任务:在xv6内核中实现写时复制fork。如果修改后的内核能同时通过cowtest和"usertests -q"的测试,那就算完成任务了。
为了帮助你测试,我们提供了一个名为cowtest的xv6程序(user/cowtest.c)。cowtest进行各种测试,但在未修改的xv6上,即使是第一个测试也会失败。因此,最初,你将看到:
$cowtest
simple: fork() failed
$
“简单”测试分配了一半以上的可用物理内存,然后fork()。fork失败,因为没有足够的空闲物理内存,无法为子进程提供父进程内存的完整副本。
当你完成任务后,内核应该通过cowtest和usertest -q中的所有测试。即:
一个合理的实验步骤如下:
1. 修改uvmcopy()将父进程的物理页映射到子进程的物理页,而不是分配新页面。清除父子进程PTE的PTE_W位。
2. 修改usertrap()以识别页面错误。当COW页面出现页面错误时,使用kalloc()分配一个新页面,将旧页面复制到新页面,然后将新页面设置到PTE中并设置PTE_W位。原本是只读的页面(没有映射PTE_W,像文本段中的页面)应该保持只读并在父和子之间共享,试图写这样一个页面的进程应该被杀死。
3. 确保每个物理页在最后一个对它的PTE引用移除时被释放。一个好的解决方案是设置一个“引用计数器”,记录每个物理页被引用的用户页表数。当kalloc()分配页时,将页的引用计数器设置为1。当fork导致子进程共享页时,增加页的引用计数,每当任何进程从其页表中删除页时,减少页的计数。kfree()只应在引用计数为零时将页放回空闲列表。可以将这些计数器保存在一个固定大小的整数数组中。你必须制定一个如何索引数组以及如何选择数组大小的方案。例如,可以用页面的物理地址除以4096作为数组的索引,并为数组提供若干元素,这些元素等于kalloc.c中的kinit()放置在空闲列表中的任意页面的最高物理地址。你可以随意修改kalloc.c(例如,kalloc()和kfree())来维护引用计数。
4. 修改copyout(),使其在遇到COW页面时使用与页面错误相同的方案。
提示
● 有一种方法可以记录每个PTE是否是COW映射,这可能很有用。你可以使用RISC-V PTE中的RSW位(保留给软件使用)来实现此目的。
● usertests -q探索了cowtest无法测试的场景,所以不要忘记检查所有测试是否都通过了这两个测试。
● kernel/riscv.h的末尾有一些有用的宏和页表标志的定义。
● 如果出现COW页错误并且没有可用内存,则应终止进程。
实验步骤
准备步骤:(1)在kernel/kalloc.c中kmem结构中增加物理内存引用计数器char ref_count[],并对该计数器定义互斥锁struct spinlock reflock,在freerange函数中对其进行初始化。
kinit函数用来初始化内存分配器。对系统中的每个物理页面以链表的形式进行管理,空闲链表保存了内核和PHYSTOP之间的每个物理页面。初始化kmem.ref_count如下。
问:为什么在初始化时设置引用计数器为1?提示:在freerange函数中调用了kfree(p), kfree(p)会减去1。
(2)修改kerbel/kalloc函数,使其在分配页面时将引用计数器设置为1。
(3)修改kernel/kfree函数,释放内存时查看引用计数是否为0,若是则释放内存,否则返回,由其他进程继续使用。
(4)增加一些辅助函数(kernel/kalloc.c)。
注意,要在defs.h中说明这些函数的原型。
(5)增加COW标志位。
在页表项中预留了2位给操作系统,这里用第8位,即#define PTE_COW (1L << 8))
kernel/riscv.h
步骤1:修改uvmcopy()。对fork函数进行修改,使其不对地址空间进行复制。fork函数会调用kernel/vm.c里的uvmcopy进行复制,因此只需要修改uvmcopy函数即可:删去uvmcopy中的kalloc函数,将子进程页表映射到父进程的物理地址,清除父子进程PTE的PTE_W位,增加COW标志位,增加物理内存引用计数。
步骤2:在usertrap函数(kernel/trap.c)中增加页面错误的处理,需判断是否是COW。这里只需要处理scause==15的情况,因为13是页面读错误,而COW是不会引起读错误的。
在这段程序数中先判断COW标志位,当该页面是COW页面时,就可以根据引用计数来进行处理。如果计数大于1,那么就需要通过kalloc申请一个新页面,然后复制内容,之后对该页面进行映射,映射的时候清除COW标志位,设置PTE_W标志位;而如果引用计数等于1,那么就不需要申请新页面,只需要对这个页面的标志位进行修改就可以了。
注:r_stval() 返回寄存器 staval 的值, 该寄存器存放引起缺页的虚拟地址。
70判断是否超出进程的地址空间,74行判断虚拟地址是否小于PGSIZE,因为在用户进程空间中,低地址端存放的是代码,此处不可修改。注:用户代码段占用多少页,如何判断?没有相关资料,但最低端的一页肯定会占用。
函数cowhandler()实现COW页处理。
注:如果把256行换成ksubref(),在测试时提示FAILED -- lost some free pages 31943 (out of 31944),看来不能替换。
步骤3:最后在copyout(kernel/vm.c)中也要增加页面错误处理,因为copyout是在内核中调用的,缺页不会进入usertrap。
某些系统调用会往COW页上写数据,因为COW页的PTE_W没有设置,就会引发缺页错误。在trap.c中规定了如果异常是从系统调用发生的,就会直接 panic。所以在copyout()的时候,如果发现了当前页是COW 页,就直接给他分配一个新的页。
在mappages函数中会触发一个remap的panic,注释掉这条语句即可,因为COW就是要对页面进行重新映射的。
测试:
现在运行make grade,看能得多少分。
参考文献:
[1]Bobrow D G, Burchfiel J D, Murphy D L, et al. TENEX, a paged time sharing system for the PDP-10[J]. Communications of the ACM, 1972, 15(3): 135-143.
[2]https://pdos.csail.mit.edu/6.1810/2023/labs/cow.html
[3]https://blog.csdn.net/pige666/article/details/108741723
[4]https://www.cnblogs.com/weijunji/p/xv6-study-9.html
[5]https://blog.csdn.net/u013577996/article/details/111972075
[6]https://blog.csdn.net/weixin_44465434/article/details/111566139
[7]https://lwn.net/Articles/849638/
相关文章:

xv6实验课程--xv6的写时复制fork(2023)
7. xv6实验课程--xv6的写时拷贝(COW)(2021) 7. xv6实验课程--xv6懒惰分页分配(lazy)(2020) 本文来源: https://mp.weixin.qq.com/s/XJkhjrlP232ZDsRyXd0oHQ 已完成的实验代码可以从下列网站获取: git clone https://gitee.com/lhwhit196…...

在Windows或Mac上安装并运行LLAMA2
LLAMA2在不同系统上运行的结果 LLAMA2 在windows 上运行的结果 LLAMA2 在Mac上运行的结果 安装Llama2的不同方法 方法一: 编译 llama.cpp 克隆 llama.cpp git clone https://github.com/ggerganov/llama.cpp.git 通过conda 创建或者venv. 下面是通过conda 创建…...

Spring底层原理学习笔记--第七讲--(初始化与销毁)
初始化与销毁 Spring提供了多种初始化和销毁手段它们的执行顺序 A07Application.java package com.lucifer.itheima.a07;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springfram…...

基于斑马算法的无人机航迹规划-附代码
基于斑马算法的无人机航迹规划 文章目录 基于斑马算法的无人机航迹规划1.斑马搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要:本文主要介绍利用斑马算法来优化无人机航迹规划。 1.斑马搜索算法 …...

干货 | 接口自动化测试分层设计与实践总结
接口测试三要素: 参数构造 发起请求,获取响应 校验结果 一、原始状态 当我们的用例没有进行分层设计的时候,只能算是一个“苗条式”的脚本。以一个后台创建商品活动的场景为例,大概流程是这样的(默认已经是登录状态下)&#…...

【Linux】服务器与磁盘补充知识,硬raid操作指南
服务器硬件 cpu 主板 内存 硬盘 网卡 电源 raid卡 风扇 远程管理卡 1.硬盘尺寸: 目前生产环境中主流的两种类型硬盘 3.5寸 和2.5寸硬盘 2.5寸硬盘可以通过使用硬盘托架后适用于3.5寸硬盘的服务器 但是3.5寸没法转换成2.5寸 2.如何在服务器上制作raid 华为服务器为例子做…...

【java】实现自定义注解校验——方法二
自定义注解校验的实现步骤: 1.创建注解类,编写校验注解,即类似NotEmpty注解 2.编写自定义校验的逻辑实体类,编写具体的校验逻辑。(这个类可以实现ConstraintValidator这个接口,让注解用来校验) 3.开启使用自定义注解进…...

算法通关村第六关|白银|二叉树的层次遍历【持续更新】
1.二叉树基本的层序遍历 仅仅遍历并输出全部元素。 List<Integer> simpleLevelOrder(TreeNode root) {if (root null) {return new ArrayList<Integer>();}List<Integer> res new ArrayList<Integer>();LinkedList<TreeNode> queue new Lin…...

vue中通过js控制scss变量
<!--* Description:* Author: 李大玄* Date: 2022-07-28 20:34:43* FilePath: /web-framework-demo/src/views/layout.vue* LastEditors: 李大玄* LastEditTime: 2022-11-01 09:25:31 --> <template><div height"100%" class"b"><inp…...

深度学习理论知识入门【EM算法、VAE算法、GAN算法】和【RBM算法、MCMC算法、HMC算法】
目录 深度学习理论知识入门首先,让我们了解第一个流程:现在,让我们看看第二个流程: EM算法GMM(高斯混合模型) 深度学习理论知识入门 首先,让我们了解第一个流程: EM(Exp…...

Java8实战-总结47
Java8实战-总结47 CompletableFuture:组合式异步编程让代码免受阻塞之苦使用定制的执行器 对多个异步任务进行流水线操作 CompletableFuture:组合式异步编程 让代码免受阻塞之苦 使用定制的执行器 就这个主题而言,明智的选择似乎是创建一个…...

功能: 在web应用程序中、读取文件
通过使用文件 API,web 内容可以要求用户选择本地文件,然后读取这些文件的内容。这种选择可以通过使用 HTML <input type"file"> 元素或通过拖放来完成。 1.通过 click() 方法使用隐藏的文件 input 元素 你可以隐藏公认难看的文件 <…...

TDD、BDD、ATDD以及SBE的概念和区别
在软件开发或是软件测试中会遇到以下这些词:TDD 、BDD 、ATDD以及SBE,这些词代表什么意思呢? 它们之间有什么关系吗? TDD 、BDD 、ATDD以及SBE的基本概念 TDD:(Test Driven Development)是一种…...

Android studio:打开应用程序闪退的问题
目录 问题描述分析原因解决方法 在开发Android应用程序的过程中遇到的问题 问题描述 在开发(或者叫测试,这么简单的程序可能很难叫开发)好一个android之后,在Android studio中调试开发好的app时,编辑器没有提示错误&a…...

Mysql数据库性能优化--performance_SCHEMA.STATEMENTS语句分析
使用performance_schema解决常见的故障案例 1 检查sql语句 使用performance_schema很容易找到引起性能问题的查询以及原因。 要启动语句检测,需要启动statement类型的插装。 插装类: statement/sql sql语句,如select,或者create table。s…...

[C/C++]数据结构 链表OJ题: 反转链表
描述: 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表 示例: 方法一: 让链表指向反向 如图所示: 代码思路: struct ListNode* reverseList(struct ListNode* head) {struct ListNode* n1NULL;struct ListNode* n2head;struct ListNode*…...

深度学习之基于YoloV5交通信号标志识别系统
欢迎大家点赞、收藏、关注、评论啦 ,由于篇幅有限,只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 基于YoloV5交通信号标志识别系统介绍 基于YoloV5的交通信号标志识别系统是一种深度学习应用,旨在通过使…...

Linux命令大全
荒诞也好,愚笨也好,总会过去的 文章目录 文件相关压缩相关tarzip 进程相关pskill 网络相关netstat IPC相关ipcsipcrm 系统资源相关topfreefdiskdfdu 权限相关umaskchmodchownchgrp 总结 文件相关 ls:列出当前目录中的文件和子目录。 ls常用…...

元宇宙是否为噱头?若不是,什么是元宇宙?他的概念、技术、应用和影响是什么?
文章来源:元宇宙的概念、技术、应用与影响——一项系统性文献综述 - 中国知网 (cnki.net) 摘要 [目的/意义]系统综述与分析当前国内外的元宇宙研究现状,有利于准确把握元宇宙发展方向,强化元宇宙基础研究,争取元宇宙建构权。[方法…...

293_C++_告警类
2、IncPos S32 AlarmList::IncPos(U32 *pu32Pos, U32 *pu32Cycle) {if((pu32Pos == NULL) || (pu32Cycle == NULL))</...

MySQL基础操作
注:mysql是大小写不敏感的. 1.数据库基础操作(展示) //1.展示当前数据库 show databases;//2.创建数据库 create database 数据库名;//3.使用数据库 use 数据库名;//4.删除数据库 drop database 数据库名;2.SQL中基本类型 2.1 数值类型(整数和浮点型) 注:decimal和numeric…...

ajax样式演示
以下是一段Ajax的演示代码,实现了通过Ajax获取后台数据并将其显示到前台页面上。 HTML文件: <!DOCTYPE html> <html><head><meta charset"UTF-8"><title>Ajax演示</title></head><body><h1>学生…...

Web前端—CSS高级(定位、高级技巧、CSS修饰属性、综合案例:购物网站轮播图)
版本说明 当前版本号[20231108]。 版本修改说明20231107初版20231108对知识点(圆点)进行补充 目录 文章目录 版本说明目录day08-CSS高级01-定位相对定位绝对定位定位居中固定定位堆叠层级 z-index定位总结 02-高级技巧CSS精灵案例-京东服务HTML结构CS…...

linux的sftp复制传输文件
连接远程服务器 sftp -P 端口号 用户名主机 例如:sftp -P 80 ubuntu172.168.0.1 并按照提示输入密码 分别使用命令查看本地当前路径(Local) 和远程路径(Remote) pwd lpwd 使用 cd 远程路径和 lcd 本地路径分别进入对…...

【星海出品】flask(一)demo
如何安装很早就讲过了,这里就省略了 创建虚拟环境 python -m venv ./venv 激活虚拟环境 source venv/Scripts/activate 退出虚拟环境 deactivate 打开一个vue项目,安装一些东西,然后启动 npm run serve npm install element-plus --save npm…...

从vue源码中看diff算法
一、v-for必须要指定key,其作用是什么? 在源码中有一个函数为,其中就是通过判断两个vnode的type和key进行判断,如果这两个属性相同,那么这两个vnode就是相同,所以在设置key的时候也不可以设置为object等无…...

【17】c++11新特性 —>弱引用智能指针weak_ptr(2)
返回管理this的shared_ptr 通过wek_ptr返回管理this资源的共享智能指针对象shared_ptr。C11中为我们提供了一个模板类叫做std::enable_shared_from_this,这个类中有一个方法叫做shared_from_this(),通过这个方法可以返回一个共享智能指针,在…...

如何去除视频水印?三种简便有效的方法解决视频水印问题
在当今社交媒体时代,视频分享已成为一种流行趋势。然而,很多人在分享自己的作品时却苦于视频上存在的水印,水印通常是出于版权保护或品牌推广的目的而添加到视频中的,但有时它们可能会对用户体验造成负面影响。 如果您正在寻找如何…...

快速构建高质量中文APP登录注册页面Figma源文件
在这个数字化时代,移动应用程序(APP)已经成为我们日常生活中不可或缺的一部分。如果您正在为您的中文APP开发登录注册页面,并寻找高质量的UI设计素材,那么您来对地方了!我们为您提供了一个完整的Figma源文件…...

MySQL库的库操作指南
1.创建数据库 一般格式:create database (if not exists) database1_name,database2_name...... 特殊形式: create database charset harset_name collate collate_name 解释: 红色字是用户自己设置的名称charset:指定数据…...