(done) MIT6.S081 2023 学习笔记 (Day6: LAB5 COW Fork)
网页:https://pdos.csail.mit.edu/6.S081/2023/labs/cow.html
任务1:Implement copy-on-write fork(hard) (完成)
现实中的问题如下:
xv6中的fork()系统调用会将父进程的用户空间内存全部复制到子进程中。如果父进程很大,复制过程可能会花费很长时间。更糟糕的是,这项工作常常是大部分浪费的:在子进程中,fork()通常会被exec()紧随其后,exec()会丢弃复制的内存,通常这些内存大部分都没有被使用。另一方面,如果父子进程都使用了一个复制的页面,并且其中一个或两个进程对该页面进行了写操作,那么这个复制就是真正需要的。
解决方案:
实现写时复制(COW)fork()的目标是将物理内存页的分配和复制推迟到真正需要这些副本的时候,如果有的话。 COW fork()只为子进程创建一个页表,用户内存的PTE指向父进程的物理页面。COW fork()将父子进程中所有的用户PTE标记为只读。当任一进程尝试写入这些COW页面时,CPU将强制产生一个页错误。内核的页错误处理程序检测到这种情况,为出错进程分配一页物理内存,将原始页面复制到新页面,并修改出错进程中的相关PTE,使其指向新页面,这次将PTE标记为可写。当页错误处理程序返回时,用户进程将能够写入其页面的副本。
COW fork()使得释放实现用户内存的物理页面变得更加复杂。一个给定的物理页面可能被多个进程的页表引用,并且只有在最后一个引用消失时才应该被释放。在像xv6这样的简单内核中,这种簿记工作相对直接,但在生产内核中,这可能会很难做对;例如,参见《Patching until the COWs come home》(修补直到COW回家)。
根据讲义,这次的测试程序是 cowtest,在调用 fork 之前,用户程序会使用多余一半的内存。因此如果没有实现 COW fork,那么 cowtest 会失败
让我们看看 cowtest 源码:
int
main(int argc, char *argv[])
{simpletest();// check that the first simpletest() freed the physical memory.simpletest();threetest();threetest();threetest();filetest();printf("ALL COW TESTS PASSED\n");exit(0);
}
如上是 main 函数,大概过了一遍 simpletest, threetest, filetest 的源码。
测试内容是:
simpletest 检测 COW 是否节约了内存、是否能够释放内存
threetest 检测 COW 是否能够在子进程写入内存时分配新的页
filetest 检测读取这些内存时是否会出错,尤其是内核的 copyout 是否能和 COW 配合完美
我们直接跟着讲义和提示写代码吧:
1.Modify uvmcopy() to map the parent’s physical pages into the child, instead of allocating new pages. Clear PTE_W in the PTEs of both child and parent for pages that have PTE_W set. (设置为只读的目的是为了以后写入的时候能够触发 page fault;父子进程都要设置为 Read-only 是因为都需要触发 page-fault,当父进程对某块内存写入时,需要分配一个相应的内存页给子进程,否则子进程会访问到父进程写入的数据)
2. Modify usertrap() to recognize page faults. When a write page-fault occurs on a COW page that was originally writeable, allocate a new page with kalloc(), copy the old page to the new page, and install the new page in the PTE with PTE_W set. Pages that were originally read-only (not mapped PTE_W, like pages in the text segment) should remain read-only and shared between parent and child; a process that tries to write such a page should be killed. (COW 不能让本来只读的页面变成可写)
3. Ensure that each physical page is freed when the last PTE reference to it goes away – but not before. A good way to do this is to keep, for each physical page, a “reference count” of the number of user page tables that refer to that page. Set a page’s reference count to one when kalloc() allocates it. Increment a page’s reference count when fork causes a child to share the page, and decrement a page’s count each time any process drops the page from its page table. kfree() should only place a page back on the free list if its reference count is zero. It’s OK to to keep these counts in a fixed-size array of integers. You’ll have to work out a scheme for how to index the array and how to choose its size. For example, you could index the array with the page’s physical address divided by 4096, and give the array a number of elements equal to highest physical address of any page placed on the free list by kinit() in kalloc.c. Feel free to modify kalloc.c (e.g., kalloc() and kfree()) to maintain the reference counts.(当对于一个页面的所有引用都消失时,再释放这一页的内存)
4. Modify copyout() to use the same scheme as page faults when it encounters a COW page. (需要对 copyout 做一些修改)
5. It may be useful to have a way to record, for each PTE, whether it is a COW mapping. You can use the RSW (reserved for software) bits in the RISC-V PTE for this. (可以用 RSW bits 来记录一个 PTE 是否是一个 COW mapping)
6. Some helpful macros and definitions for page table flags are at the end of kernel/riscv.h. (kernel/riscv.h 里的内容可能有用)
7. If a COW page fault occurs and there’s no free memory, the process should be killed. (当发生 COW page fault 但没有足够内存时,进程应该被杀掉)
自己的提示:
在做 LAB3 : pagetable 的时候遇到过这么一张图,在这里也很有用

RSW bits 是 8~9
开始写代码:
1.在 vm.c : uvmcopy 添加 printf 打印 PTEs,发现 RSW bits 一直都是 0,说明平时不用都是置为 0
2.在 vm.c : uvmcopy 去掉分配内存和拷贝内存内容的部分,仅仅保留拷贝映射的部分。同时,设置老新页表的 RSW bits = original WR bits。此外,错误处理中去掉释放内存的部分,仅保留删除映射的部分。
3.在 trap.c : usertrap 中新添一个分支 “if scause == 0xf”。(0xd 是 load page fault,0xf 是 store page fault)使用 stval 寄存器的值作为触发 page fault 的页面起始地址va。使用 RSW bits 判断是否属于 COW pages,同时判断原来的权限。如果不属于 COW pages,直接 kill 掉;如果属于且本来可写,那么使用 kalloc 分配页面并设置为可写;如果属于 COW pages 但本来不可写,那么直接 kill 掉。如果使用 kalloc 由于内存不足失败,那么直接 kill 掉。这里注意使用 kalloc 分配页面成功时,修改 PTE 映射时要 clear RSW bits,否则以后该页被用户程序设置为只读时,发生 page-fault 会使用残留的 RSW bits 设置为可写。
4.在 kalloc.c 中维护一个全局的 reference_count 数组,大小为 [(PHYSTOP - KERNBASE) / PGSIZE]。在 kalloc 中,根据分配的页面的起始物理地址,把 reference_count 数组中对应元素设置为 1。把 mappages 包装出一个新的函数 uvmmap,替换掉原来调用 mappages 的地方。在 uvmcopy 中增加 reference_count 数组元素 (一个进程结束后,由于这个进程分配的页就应该回到 freelist,所以只考虑用户进程计算引用数,不考虑内核引用)。在 kfree 中减少 reference_count 数组元素,到 0 时真正释放内存(原因是,fork 后,多个程序会调用多次 kfree,所以在 kfree 中计数)。第一次调用 kfree (kinit) 的时候还没有调用过 kalloc,因此拷贝一个 kfree_initialize 来替换 kinit 中的 kfree
5.由于 copyout 会在内核态发生对 COW page 写入的操作,所以这里也要进行 page fault 的处理。如果发现是 COW page,为了不影响其它进程访问到的内存内容,我们调用 kalloc 申请一个新的页,然后拷贝原始内容,修改页表 … (跟 page fault 很相似)
出现内存泄漏的时候,可以使用 gdb watch 去观察 reference_count 数组的某些元素,找到修改这些元素的代码,这样能帮助我们快速调试。
出现内存泄漏的时候,调试思路为搞清楚几个问题:1.哪些内存地址没有被释放? 2.这些没被释放的内存地址是什么时候被分配的?3.它们为什么没有被释放?
一个很容易出错的地方:使用 fork 后,父子进程的 PTE 的 PTE_W 都要 clear,这是为了防止父进程对内存进行修改后,子进程访问到父进程修改的内容。那么此时有一个问题,父子进程都发生 store page fault 并且申请了新的 kalloc() 后,那么此时申请了两个新页,一开始的那个页就悬空泄露了,会造成内存泄漏。
个人感觉这次 LAB 最容易出 bug 的地方就是内存泄漏。
处理完后,运行 cowtest 很顺利如下:

再运行 usertests,发现报错,经过观察,发现只是对代码的修改导致代码在遇到错误情况时(比如内存不足或写入一个超高地址时应该返回 -1,或者直接把用户进程杀掉)没有返回正确的错误码,稍作调整即可。再次测试,顺利,如下:

所有 usertests 顺利通过
运行 make grade,所有测试顺利通过

相关文章:
(done) MIT6.S081 2023 学习笔记 (Day6: LAB5 COW Fork)
网页:https://pdos.csail.mit.edu/6.S081/2023/labs/cow.html 任务1:Implement copy-on-write fork(hard) (完成) 现实中的问题如下: xv6中的fork()系统调用会将父进程的用户空间内存全部复制到子进程中。如果父进程很大,复制过程…...
SYN Flooding的攻击原理
SYN Flooding是一种常见的网络攻击方式,属于拒绝服务攻击(DoS)的一种,其攻击原理主要是利用了TCP协议的三次握手过程,以下是具体介绍: TCP三次握手正常流程 第一次握手:客户端向服务器发送一个…...
MYSQL--一条SQL执行的流程,分析MYSQL的架构
文章目录 第一步建立连接第二部解析 SQL第三步执行 sql预处理优化阶段执行阶段索引下推 执行一条select 语句中间会发生什么? 这个是对 mysql 架构的深入理解。 select * from product where id 1;对于mysql的架构分层: mysql 架构分成了 Server 层和存储引擎层&a…...
cmd命令行无法进入D:盘怎么办
我找到了一个方法就是 增加一个/d cd /d d: 如下图,我不仅可以进入d盘符下,还可以访问盘符下的文件夹...
CRC校验详解
CRC校验即循环冗余校验(Cyclic Redundancy Check),是基于数据计算一组效验码,用于核对数据传输过程中是否被更改或传输错误。首先看两个概念,后续会用到。 模2除法:也叫模2运算,就是结果除以2后取余数。模2除法每一位除的结果不影响其它位,即不向上一位借位,所以实际…...
windows系统本地部署deepseek及webui界面
一、官网下载ollama 二、使用ollama下载deepseek r1模型 根据显存选择多少b的参数的模型 ollama run deepseek-r1:32b 三、安装conda curl -O https://repo.anaconda.com/miniconda/Miniconda3-latest-Windows-x86_64.exe Miniconda3-latest-Windows-x86_64.exe 四、构建…...
(算法竞赛)使用广度优先搜索(BFS)解决迷宫最短路径问题
在这个充满奇思妙想的世界里,每一次探索都像是打开了一扇通往新世界的大门。今天,我们将踏上一段特别的旅程,去揭开那些隐藏在代码、算法、数学谜题或生活智慧背后的秘密。🎉😊 所以,系好安全带࿰…...
Sqoop源码修改:增加落地HDFS文件数与MapTask数量一致性检查
个人博客地址:Sqoop源码修改:增加落地HDFS文件数与MapTask数量一致性检查 | 一张假钞的真实世界 本篇是对记录一次Sqoop从MySQL导入数据到Hive问题的排查经过的补充。 Sqoop 命令通过 bin 下面的脚本调用,调用如下: exec ${HAD…...
嵌入式系统|DMA和SPI
文章目录 DMA(直接内存访问)DMA底层原理1. 关键组件2. 工作机制3. DMA传输模式 SPI(串行外设接口)SPI的基本原理SPI连接示例 DMA与SPI的共同作用 DMA(直接内存访问) 类型:DMA是一种数据传输接口…...
leetcode——将有序数组转化为二叉搜索树(java)
给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 平衡 二叉搜索树。 示例 1: 输入:nums [-10,-3,0,5,9] 输出:[0,-3,9,-10,null,5] 解释:[0,-10,5,null,-3,null,9] 也将被视为正确答…...
冯诺依曼结构和进程概念及其相关的内容的简单介绍
目录 编辑 冯诺依曼体系结构 操作系统(Operator System) 进程 引入 基本概念 描述进程-PCB task_ struct内容分类 进程 ID (PID)和查看进程 进程状态: 进程创建: 进程终止: 进程间通信 (IPC): 冯诺依曼体系结构 冯诺依曼体系结构是现代计算机的基础架构…...
Native Memory Tracking 与 RSS的差异问题
一 问题现象 前一段时间用nmt查看jvm进程的栈区占用的内存大小。测试代码如下 public class ThreadOOM {public static void main(String[] args) {int i 1;while (i < 3000) {Thread thread new TestThread();thread.start();System.out.println("thread : "…...
在K8s中部署动态nfs存储provisioner
背景 之前,我已经在一台worker node上安装了local lvm 的provisioner来模拟需要本地高IOPS的数据库等stafeful应用的实现。 为了后续给虚拟机里的K8s集群安装可用的metrics和logs监控系统(metrics和logs的时序数据库需要永久存储)࿰…...
家庭财务管理系统的设计与实现
标题:家庭财务管理系统的设计与实现 内容:1.摘要 摘要:随着家庭经济的日益复杂,家庭财务管理变得越来越重要。本文旨在设计并实现一个功能强大的家庭财务管理系统,以帮助用户更好地管理家庭财务。通过对家庭财务管理需求的分析,我…...
数据结构-Stack和栈
1.栈 1.1什么是栈 栈是一种特殊的线性表,只允许在固定的一段进行插入和删除操作,进行插入和删除操作的一段称为栈顶,另一端称为栈底。 栈中的数据元素遵顼后进先出LIFO(Last In First Out)的原则,就像一…...
使用vhd虚拟磁盘安装两个win10系统
使用vhd虚拟磁盘安装两个win10系统 前言vhd虚拟磁盘技术简介准备工具开始动手实践1.winX选择磁盘管理2.选择“操作”--“创建VHD”3.自定义一个位置,输入虚拟磁盘大小4.右键初始化磁盘5.选择GPT分区表格式6.右键新建简单卷7.给卷起个名字,用于区分8.打开…...
代码随想录34 动态规划
1.经典问题: 背包问题 打家劫舍 斐波那契数列 爬楼梯问题 股票问题 2.dp数组以及下标的含义 3.递推公式 3.dp数组初始化 4.遍历顺序 5.打印数组 leetcode509.斐波那契数列 1.确定dp[i]含义 dp[i]第i个斐波那契数的值为dp[i] 2.递推公式:dp[…...
【2025年最新版】Java JDK安装、环境配置教程 (图文非常详细)
文章目录 【2025年最新版】Java JDK安装、环境配置教程 (图文非常详细)1. JDK介绍2. 下载 JDK3. 安装 JDK4. 配置环境变量5. 验证安装6. 创建并测试简单的 Java 程序6.1 创建 Java 程序:6.2 编译和运行程序:6.3 在显示或更改文件的…...
Shell特殊状态变量以及常用内置变量总结
目录 1. 特殊的状态变量 1.1 $?(上一个命令的退出状态) 1.2 $$(当前进程的 PID) 1.3 $!(后台进程的 PID) 1.4 $_(上一条命令的最后一个参数) 2.常用shell内置变量 2.1 echo&…...
【4Day创客实践入门教程】Day4 迈向高手之路——进一步学习!
Day4 迈向高手之路——进一步学习! 目录 Day4 迈向高手之路——进一步学习!更多的开发板外壳制作 Day0 创想启程——课程与项目预览Day1 工具箱构建——开发环境的构建Day2 探秘微控制器——单片机与MicroPython初步Day3 实战演练——桌面迷你番茄钟Day4…...
未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...
DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
接口自动化测试:HttpRunner基础
相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具,支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议,涵盖接口测试、性能测试、数字体验监测等测试类型…...
宇树科技,改名了!
提到国内具身智能和机器人领域的代表企业,那宇树科技(Unitree)必须名列其榜。 最近,宇树科技的一项新变动消息在业界引发了不少关注和讨论,即: 宇树向其合作伙伴发布了一封公司名称变更函称,因…...
软件工程 期末复习
瀑布模型:计划 螺旋模型:风险低 原型模型: 用户反馈 喷泉模型:代码复用 高内聚 低耦合:模块内部功能紧密 模块之间依赖程度小 高内聚:指的是一个模块内部的功能应该紧密相关。换句话说,一个模块应当只实现单一的功能…...
企业大模型服务合规指南:深度解析备案与登记制度
伴随AI技术的爆炸式发展,尤其是大模型(LLM)在各行各业的深度应用和整合,企业利用AI技术提升效率、创新服务的步伐不断加快。无论是像DeepSeek这样的前沿技术提供者,还是积极拥抱AI转型的传统企业,在面向公众…...
