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))</...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...
[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...
毫米波雷达基础理论(3D+4D)
3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文: 一文入门汽车毫米波雷达基本原理 :https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
消息队列系统设计与实践全解析
文章目录 🚀 消息队列系统设计与实践全解析🔍 一、消息队列选型1.1 业务场景匹配矩阵1.2 吞吐量/延迟/可靠性权衡💡 权衡决策框架 1.3 运维复杂度评估🔧 运维成本降低策略 🏗️ 二、典型架构设计2.1 分布式事务最终一致…...
spring Security对RBAC及其ABAC的支持使用
RBAC (基于角色的访问控制) RBAC (Role-Based Access Control) 是 Spring Security 中最常用的权限模型,它将权限分配给角色,再将角色分配给用户。 RBAC 核心实现 1. 数据库设计 users roles permissions ------- ------…...
简单介绍C++中 string与wstring
在C中,string和wstring是两种用于处理不同字符编码的字符串类型,分别基于char和wchar_t字符类型。以下是它们的详细说明和对比: 1. 基础定义 string 类型:std::string 字符类型:char(通常为8位)…...
PLC入门【4】基本指令2(SET RST)
04 基本指令2 PLC编程第四课基本指令(2) 1、运用上接课所学的基本指令完成个简单的实例编程。 2、学习SET--置位指令 3、RST--复位指令 打开软件(FX-TRN-BEG-C),从 文件 - 主画面,“B: 让我们学习基本的”- “B-3.控制优先程序”。 点击“梯形图编辑”…...
