(undone) 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) (doing)

现实中的问题如下:
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() 后,那么此时申请了两个新页,一开始的那个页就悬空泄露了,会造成内存泄漏。

TODO: here


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/69556.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

分享| RL-GPT 框架通过慢agent和快agent结合提高AI解决复杂任务的能力-Arxiv

结论 “RL-GPT: Integrating Reinforcement Learning and Code-as-policy” RL-GPT 框架为解决大语言模型在复杂任务处理中的难题提供了创新有效的途径, 旨在将强化学习(RL)和代码即策略相结合, 以解决大语言模型&#xff08…

PPT自动化 python-pptx -7: 占位符(placeholder)

占位符(placeholder)是演示文稿中用于容纳内容的预格式化容器。它们通过让模板设计者定义格式选项,简化了创建视觉一致幻灯片的过程,同时让最终用户专注于添加内容。这加快了演示文稿的开发速度,并确保幻灯片之间的外观…

【Linux权限】—— 于虚拟殿堂,轻拨密钥启华章

欢迎来到ZyyOvO的博客✨,一个关于探索技术的角落,记录学习的点滴📖,分享实用的技巧🛠️,偶尔还有一些奇思妙想💡 本文由ZyyOvO原创✍️,感谢支持❤️!请尊重原创&#x1…

一个简单的自适应html5导航模板

一个简单的 HTML 导航模板示例&#xff0c;它包含基本的导航栏结构&#xff0c;同时使用了 CSS 进行样式美化&#xff0c;让导航栏看起来更美观。另外&#xff0c;还添加了一些 JavaScript 代码&#xff0c;用于在移动端实现导航菜单的展开和收起功能。 PHP <!DOCTYPE htm…

【算法应用】基于A*-蚁群算法求解无人机城市多任务点配送路径问题

目录 1.A星算法原理2.蚁群算法原理3.结果展示4.代码获取 1.A星算法原理 A*算法是一种基于图搜索的智能启发式算法&#xff0c;它具有高稳定性和高节点搜索效率。主要原理为&#xff1a;以起点作为初始节点&#xff0c;将其加入开放列表。从开放列表中选择具有最小总代价值 f (…

自制一个入门STM32 四足机器人具体开发顺序

0 前期准备 1. 知识储备 学习 STM32 微控制器的基础知识&#xff0c;包括 GPIO、定时器、串口通信等外设的使用&#xff0c;可通过官方文档、教程和视频课程进行学习。了解舵机控制原理&#xff0c;因为四足机器人通常使用舵机来实现关节运动。掌握基本的机械结构设计知识&am…

洛谷P11464 支配剧场

支配剧场 题目背景 May all the beauty be blessed. 题目描述 布洛妮娅和符华在寻找琪亚娜的途中&#xff0c;被支配之律者困在了支配剧场的高塔回廊之中。布洛妮娅敏锐地发现&#xff0c;虚无回廊是由一些支配之律者生成的积木构成的&#xff0c;只要击碎其中一些积木&#…

http3网站的设置(AI不会配,得人工配)

堡塔PHP项目中配置nginx1.26.0设置http3协议 # 文件所在服务器中的路径 /www/server/nginx/conf/nginx.confuser www www; worker_processes auto; error_log /www/wwwlogs/nginx_error.log crit; pid /www/server/nginx/logs/nginx.pid; worker_rlimit_nofile 512…

Python-基于PyQt5,json和playsound的通用闹钟

前言&#xff1a;刚刚结束2024年秋季学期的学习&#xff0c;接下来我们继续来学习PyQt5。由于之前我们已经学习了PyQt5以及PyUIC,Pyrcc和QtDesigner的安装&#xff0c;配置。所以接下来我们一起深入PyQt5&#xff0c;学习如何利用PyQt5进行实际开发-基于PyQt5&#xff0c;json和…

预测不规则离散运动的下一个结构

有一个点在19*19的平面上运动&#xff0c;运动轨迹为 一共移动了90步&#xff0c;顺序为 y x y x y x 0 17 16 30 10 8 60 15 15 1 3 6 31 10 7 61 14 15 2 12 17 32 9 9 62 16 15 3 4 12 33 10 9 63 18 15 4 3 18 34 15 12 6…

固有频率与模态分析

目录 引言 1. 固有频率&#xff1a;物体的“天生节奏” 1.1 定义 1.2 关键特点 1.3 实际意义 2. 有限元中的模态分析&#xff1a;给结构“体检振动” 2.1 模态分析的意义 2.2 实际案例 2.2.1 桥梁模态分析 2.2.2 飞机机翼模态分析 2.2.3 具体事例 3. 模态分析的工具…

供应链系统设计-供应链中台系统设计(十)- 清结算中心概念片篇

综述 我们之前在供应链系统设计-中台系统设计系列&#xff08;五&#xff09;- 供应链中台实践概述文章中针对中台到底是什么进行了描述&#xff0c;对于中台的范围也进行划分&#xff0c;如下图所示&#xff1a; 关于商品中心&#xff0c;我们之前用4篇文章介绍了什么是商品中…

C27.【C++ Cont】时间、空间限制和STL库的简单了解

&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;春节篇&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8; 目录 1.竞赛中的…

步进电机加减速公式推导

运动控制梯形速度曲线相关算法请参考下面系列文章 PLC运动控制基础系列之梯形速度曲线_三菱运动控制模块梯形加减速-CSDN博客文章浏览阅读3.1k次,点赞3次,收藏7次。本文是关于PLC运动控制的基础教程,重点介绍了梯形速度曲线的概念、计算和应用。讨论了梯形加减速在启动和停…

指针的介绍3后

1.函数指针变量 1.1函数的地址 void test(int (*arr)[2]) {printf("zl_dfq\n"); } int main() {printf("%p\n", test);printf("%p\n", &test);return 0; } 由上面的程序运行可知&#xff1a; 函数名就是函数的地址 &函数名也可以拿到函…

春晚舞台上的人形机器人:科技与文化的奇妙融合

文章目录 人形机器人Unitree H1的“硬核”实力传统文化与现代科技的创新融合网友热议与文化共鸣未来展望&#xff1a;科技与文化的更多可能结语 2025 年央视春晚的舞台&#xff0c;无疑是全球华人目光聚焦的焦点。就在这个盛大的舞台上&#xff0c;一场名为《秧BOT》的创意融合…

连接 OpenAI 模型:基础操作

在这一部分中&#xff0c;我们将介绍如何连接 OpenAI 模型&#xff0c;设置 API 密钥&#xff0c;并使用 Spring AI 的 ChatClient 与 OpenAI 模型进行简单的对话。Spring AI 为集成 OpenAI 模型提供了方便的工具&#xff0c;使得开发者能够更轻松地与 GPT 系列模型进行交互。 …

CMake常用命令指南(CMakeList.txt)

CMakeList从入门到精通的文章有很多不再赘述&#xff08; 此处附带一篇优秀的博文链接&#xff1a;一个简单例子&#xff0c;完全入门CMake语法与CMakeList编写 &#xff09;。 本文主要列举 CMake 中常用命令的详细说明、优缺点分析以及推荐做法&#xff0c;以更好地理解和灵…

zsh安装插件

0 zsh不仅在外观上比较美观&#xff0c;而且其具有强大的插件&#xff0c;如果不使用那就亏大了。 官方插件库 https://github.com/ohmyzsh/ohmyzsh/wiki/Plugins 官方插件库并不一定有所有的插件&#xff0c;比如zsh-autosuggestions插件就不再列表里&#xff0c;下面演示zs…

消息队列篇--通信协议篇--应用层协议和传输层协议理解

在网络通信中&#xff0c;传输层协议和应用层协议是OSI模型中的两个不同层次的协议&#xff0c;它们各自承担着不同的职责。 下文中&#xff0c;我们以TCP/UDP&#xff08;传输层协议&#xff09;和HTTP/SMTP&#xff08;应用层协议&#xff09;为例进行详细解释。 1、传输层协…