(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…...

简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...

ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

【JavaWeb】Docker项目部署
引言 之前学习了Linux操作系统的常见命令,在Linux上安装软件,以及如何在Linux上部署一个单体项目,大多数同学都会有相同的感受,那就是麻烦。 核心体现在三点: 命令太多了,记不住 软件安装包名字复杂&…...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

论文阅读:Matting by Generation
今天介绍一篇关于 matting 抠图的文章,抠图也算是计算机视觉里面非常经典的一个任务了。从早期的经典算法到如今的深度学习算法,已经有很多的工作和这个任务相关。这两年 diffusion 模型很火,大家又开始用 diffusion 模型做各种 CV 任务了&am…...