MIT6.828LAB4 (3)

LAB3_Part B: Copy-on-Write Fork

文章目录

  • LAB3_Part B: Copy-on-Write Fork
  • 前言
  • 练习8
  • 练习9
  • 练习10
  • 练习11
  • 练习12
  • 总结


前言

记录一下自己的学习过程
实验内容翻译:
https://gitee.com/cherrydance/mit6.828
该翻译仅供参考

练习8

实现sys_env_set_pgfault_upcall系统调用。在查找目标环境的环境ID时,请确保启用权限检查,因为这是一个"危险"的系统调用。

1,判断envid合法
2,将func赋给env_pagfault_upcall

代码如下:

	struct Env * env_store = NULL;
	if(envid2env(envid, &env_store, 1) < 0){
		return -E_BAD_ENV;
	}
	env_store->env_pgfault_upcall = func;
	return 0;

最后在syscall中添加系统调用:

	case SYS_env_set_pgfault_upcall:
		ret = sys_env_set_pgfault_upcall((envid_t)a1, (void *)a2);
		break;

练习9

在kern/trap.c中实现page_fault_handler中所需的代码,以将页错误分派到用户模式处理程序。在向异常堆栈写入数据时,请采取适当的预防措施。(如果用户环境在异常堆栈上的空间用完会发生什么?)

1,检查当前环境是否注册了页错误处理程序,如果没注册则按之前的步骤销毁环境
2,检查当前的tf->tf_esp是否位于用户异常栈中,以此判断是不是第一次使用页错误处理程序
3,根据要求在栈中获取一个UTrapframe大小的空间,如果是递归还需要额外空出4字节
4,使用user_mem_assert判断是否内存溢出
5,保存tf的信息在UTrapframe之中
6,调用页错误处理程序,tf->tf_esp设置为程序返回地址,即UTrapframe的底部
7,处理完页错误之后继续运行当前环境

实现代码如下:

	if(curenv->env_pgfault_upcall != NULL){
		struct UTrapframe * utrapf = NULL;
		if(tf->tf_esp >= UXSTACKTOP-PGSIZE && tf->tf_esp < UXSTACKTOP){
			utrapf = (struct UTrapframe *)(tf->tf_esp - sizeof(struct UTrapframe) - 4);
		}else{
			utrapf =(struct UTrapframe *)(UXSTACKTOP - sizeof(struct UTrapframe));
		}
		user_mem_assert(curenv, (void *)utrapf, sizeof(struct UTrapframe), PTE_W);
		utrapf->utf_esp = tf->tf_esp;
		utrapf->utf_eflags = tf->tf_eflags;
		utrapf->utf_eip = tf->tf_eip;
		utrapf->utf_regs = tf->tf_regs;
		utrapf->utf_err = tf->tf_trapno;
		utrapf->utf_fault_va = fault_va;
		tf->tf_eip = (uintptr_t)curenv->env_pgfault_upcall;
		tf->tf_esp = (uintptr_t)utrapf;
		env_run(curenv);
		return;
	}

练习10

在lib/pfentry.S中实现_pgfault_upcall例程。最有趣的部分是返回到导致页错误的用户代码中的原始位置。您将直接返回到那里,而无需通过内核返回。难点在于同时切换堆栈和重新加载EIP。

如图,是对汇编操作的部分描述
在这里插入图片描述

	addl $8, %esp
	#弹出两个参数fault_va和tr_err
 	movl 0x20(%esp), %eax 
 	#将该地址向上加0x20,此时为eip的地址,将值赋给eax     
    subl $4, 0x28(%esp)
    #获取utf_esp的地址,将其减去4得到的是异常栈的顶部或者空         
    movl 0x28(%esp), %edx    
    #将这个地址的内容存到edx
    movl %eax, (%edx)             
    #把eip存到栈底或者之前为空的位置
    popal
    #弹出通用寄存器             
    addl $4, %esp
    #弹出eip      
    popfl
    #弹出eflags
    popl %esp
    #弹出esp
    ret 
    此时返回的地址是之前eip存入的地址

自己画一下图就能清楚的理解这个过程

练习11

完成lib/pgfault.c中的set_pgfault_handler()函数

1,使用系统调用分配一页
2,注册页错误处理函数

代码如下:

	if(sys_page_alloc(sys_getenvid(), (void *) (UXSTACKTOP - PGSIZE), PTE_SYSCALL) < 0){
		panic("sys_page_alloc failure in set_pgfault_handler\n");
	}
       if(sys_env_set_pgfault_upcall(sys_getenvid(), _pgfault_upcall) < 0){
		panic("sys_env_set_pgfault_upcall failure in set_pgfault_handler\n");
	}

确保运行结果与要求的一致。
faultalloc.c和faultallocbad.c的区别就是前者使用cprintf是用户级的函数,后者用到sys_cputs是系统调用函数。

练习12

在 lib/fork.c 中实现 fork、duppage 和 pgfault。
使用 forktree 程序测试你的代码。它应该会产生以下消息,并夹杂着 ‘new env’、‘free env’ 和 ‘exiting gracefully’ 的消息。这些消息可能不会按照这个顺序出现,并且环境的 ID 可能会不同。

首先实现pgfault,最先是要判断数据的合法性,即检查错误是否为写操作(检查错误代码中的 FEC_WR 标志),并且页面的页表项是否标记为 PTE_COW。如果不是,则触发 panic。这里提示我们使用uvpt来判断页表项是否标记为PTE_COW,那uvpt的含义到底如何呢。先看链接https://pdos.csail.mit.edu/6.828/2018/labs/lab4/uvpt.html,这里有初步介绍,我感觉这里最关键的一步反而在有没有认识到uvpt[]这个是看成虚拟地址,MMU在后面怎么处理这个虚拟地址去获得最终的值。这个函数的关键点也就在这。

1,判断FEC_ER和PTE_COW和PTE_P
2,获取环境id
3,分配一个可写的临时页面
4,将出错的页面内容复制到可写页面
5,将可写页面复制到出错的位置
6,取消可写页面的映射
	if(!(err & FEC_WR) || !(uvpt[PGNUM(addr)] & PTE_COW)){
		panic("pgfault :err is not TEC_WR");
	}
	if(!(uvpd[PDX(addr)] & PTE_P) || !(uvpt[PGNUM(addr)] & PTE_P)){
		panic("pgfault :err is not PTE_P\n");
	}
	envid_t e_id = sys_getenvid();
	addr = ROUNDDOWN(addr, PGSIZE);
	if(sys_page_alloc(e_id, PFTEMP, PTE_W | PTE_U | PTE_P) < 0){
		panic("pgfault: sys_page_alloc failed.");
	}
	memcpy(PFTEMP, addr, PGSIZE);
	if(sys_page_map(e_id, PFTEMP, e_id, addr, PTE_U | PTE_W | PTE_P) < 0) {
		panic("pgfault: sys_page_map failed."); 
	}
	
	if(sys_page_unmap(e_id, PFTEMP) < 0){
		panic("pgfault: sys_page_unmap failed.");
	}

对于函数duppage:
它的作用是将我们的虚拟页 pn(地址为 pn*PGSIZE)映射到目标 envid 的相同虚拟地址上。

1,如果页面是可写或者写时复制,则以写时复制的方式复制给子环境
2,此时要将父亲页面也以写时复制的方式重新映射
2,如何是只读则以只读的方式复制
	if (uvpt[pn] & PTE_W || uvpt[pn] & PTE_COW) {
		if(sys_page_map(0, (void *)(pn * PGSIZE), envid, (void *)(pn * PGSIZE), PTE_U | PTE_P | PTE_COW) < 0){
			panic("duppage: sys_page_map failure");
		}

		if(sys_page_map(0, (void *)(pn * PGSIZE), 0, (void *)(pn * PGSIZE), PTE_U | PTE_P | PTE_COW) < 0){
			panic("duppage: sys_page_map failure");
		}

	}else {
		if(sys_page_map(0, (void *)(pn * PGSIZE), 0, (void *)(pn * PGSIZE), PTE_U | PTE_P) < 0){
			panic("duppage: sys_page_map failure");
		}	
	}

fork()函数:
使用写时复制(copy-on-write)实现用户级别的 fork。

1,父进程使用上面实现的 set_pgfault_handler() 函数将 pgfault() 设置为 C 级别的页面错误处理程序。
2,父进程调用 sys_exofork() 创建一个子进程环境。
3,按要求复制USTACKTOP下的页面
4,给子进程分配一个异常栈
5,将子进程的页错误入口设置为与父进程相同
6,将子进程的运行状态设置为可运行
	extern void _pgfault_upcall(void);
	set_pgfault_handler(pgfault);
	envid_t envid = sys_exofork();
	if(envid < 0){
		panic("fork: sys_exfork() failure");
	}
	if(envid == 0){
		thisenv = envs + ENVX(sys_getenvid());
		return 0;
	}
	size_t i;
	for(i = 0; i < USTACKTOP; i+=PGSIZE){
		if((uvpd[PDX(i)] & PTE_P) && (uvpt[PGNUM(i)] & PTE_P) && (uvpt[PGNUM(i)] & PTE_U)){
			duppage(envid,PGNUM(i));
		}
	}
	if (sys_page_alloc(envid, (void *)(UXSTACKTOP - PGSIZE), PTE_U | PTE_W | PTE_P) < 0){
		panic("fork: sys_page_alloc failed.");
	}
	sys_env_set_pgfault_upcall(envid, _pgfault_upcall);
	if (sys_env_set_status(envid, ENV_RUNNABLE) < 0){
		panic("fork: sys_env_set_status failed.");
	}
	return envid;

运行结果:
在这里插入图片描述

总结

完成了PartB,但感觉非常模糊。先酱紫

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

内存映射mmap拓展

第一个是上一篇博客中用mmap实现任意两个进程间相互通信&#xff0c;前几篇博客也用管道实现了进程间通信 这里有个问题&#xff0c;管道是基于缓冲区环形队列的&#xff0c;实验也表明读过的数据不能在读利用命名管道实现任意进程间的通信 那么mmap多个读写操作时会是什么情况…

关于GPU显卡的介绍

一.关于英伟达历代产品架构 显卡是一种计算机硬件设备,也被称为显示适配器或图形处理器。目前的硬件部分主要由主板、芯片、存储器、散热器&#xff08;散热片、风扇&#xff09;等部分。显卡的主要芯片是显卡的主要处理单元。显卡上也有和计算机存储器相似的存储器&#xff0…

Mysql数据库-基本表操作

1.表操作 创建表&#xff1a;CREATE TABLE table_name ( field1 datatype, field2 datatype, field3 datatype ) character set 字符集 collate 校验规则 engine 存储引擎; field 表示列名 datatype 表示列的类型 character set 字符集&#xff0c;如果没有指定字符集&#xff…

python学习笔记------集合(set)

集合定义格式 基本语法&#xff1a; #定义集合字面量 {元素&#xff0c;元素&#xff0c;元素......&#xff0c;元素} #定义集合变量 变量名称{元素&#xff0c;元素&#xff0c;元素......&#xff0c;元素} #定义空集合 变量名称set() #定义集合字面量 {元素&#…

【AI辅助研发】-趋势:大势已来,行业变革

【AI辅助研发】-趋势&#xff1a;大势已来&#xff0c;行业变革 引言 在科技日新月异的今天&#xff0c;人工智能&#xff08;AI&#xff09;技术已逐渐渗透到各行各业&#xff0c;其中软件研发行业更是受益匪浅。AI辅助研发已成为大势所趋&#xff0c;不仅提高了软件开发的效…

登录凭证------

为什么需要登录凭证&#xff1f; web开发中&#xff0c;我们使用的协议http是无状态协议&#xff0c;http每次请求都是一个单独的请求&#xff0c;和之前的请求没有关系&#xff0c;服务器就不知道上一步你做了什么操作&#xff0c;我们需要一个办法证明我没登录过 制作登录凭…

免费无水印视频素材哪里下载?这几个地方您要知道

哟哟&#xff0c;切克闹&#xff0c;视频剪辑达人们&#xff0c;是不是在视频素材的海洋里迷航了&#xff1f;别着急&#xff0c;今天我就给大家分享几个超实用的无水印短视频素材合集网&#xff0c;让你的创作更加得心应手&#xff0c;从此素材不再是你的烦恼 1&#xff0c;蛙…

鸿蒙原生应用元服务开发-WebGL网页图形库开发概述

WebGL的全称为Web Graphic Library(网页图形库)&#xff0c;主要用于交互式渲染2D图形和3D图形。目前HarmonyOS中使用的WebGL是基于OpenGL裁剪的OpenGL ES&#xff0c;可以在HTML5的canvas元素对象中使用&#xff0c;无需使用插件&#xff0c;支持跨平台。WebGL程序是由JavaScr…

Hudi入门

一、Hudi编译安装 1.下载 https://archive.apache.org/dist/hudi/0.9.0/hudi-0.9.0.src.tgz2.maven编译 mvn clean install -DskipTests -Dscala2.12 -Dspark33.配置spark与hudi依赖包 [rootmaster hudi-spark-jars]# ll total 37876 -rw-r--r-- 1 root root 38615211 Oct …

【Python从入门到进阶】50、当当网Scrapy项目实战(三)

接上篇《49、当当网Scrapy项目实战&#xff08;二&#xff09;》 上一篇我们讲解了的Spider与item之间的关系&#xff0c;以及如何使用item&#xff0c;以及使用pipelines管道进行数据下载的操作&#xff0c;本篇我们来讲解Scrapy的多页面下载如何实现。 一、多页面下载原理分…

【Leetcode每日一刷】滑动窗口:209.长度最小的子数组

一、209.长度最小的子数组 1.1&#xff1a;题目 题目链接 1.2&#xff1a;解题思路 题型&#xff1a;滑动窗口&#xff1b;时间复杂度&#xff1a;O(n) &#x1faa7; 滑动窗口本质也是双指针的一种技巧&#xff0c;特别适用于字串问题 ❗❗核心思想/ 关键&#xff1a;左右…

A5自媒体wordpress主题模板

一个简洁的wordpress个人博客主题&#xff0c;适合做个人博客&#xff0c;SEO优化效果挺不错的。 https://www.wpniu.com/themes/204.html

前端学习之行内和块级标签

行内标签 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>span</title> </head> <body><!-- 行内标签特点&#xff1a;1、不换行,一行可以放多个2、默认宽度内容撑开代表&#…

谈谈我的自媒体创作真实感悟

因为我有数十款APP和小程序&#xff0c;基本都是些辅助内容创作的工具&#xff0c;于是我就顺水推舟做了几个自媒体账号&#xff1a;微信公众号&#xff0c;抖音&#xff0c;知乎&#xff0c;小红书&#xff0c;CSDN等&#xff0c;账号的名字都是全赞工程师。 目前这些号有收入…

分享一些实用性的大语言模型(GitHub篇)

1.多模态大模型 GitHub网址&#xff1a;haotian-liu/LLaVA&#xff1a;[NeurIPS23 Oral] 视觉指令调优 &#xff08;LLaVA&#xff09; 构建&#xff0c;旨在实现 GPT-4V 级别及以上的能力。 (github.com) 下面是LLaVA模型的介绍&#xff0c;作者都有一直维护和更新&#xff0c…

CSS居中对齐 (垂直居中)

内部块级元素的高度要小于容器(父元素) 方案一&#xff1a;行高 容器高度&#xff08;单行内联元素&#xff09; 限制条件&#xff1a;仅用于单行内联元素 display:inline 和 display: inline-block; 给容器添加样式 height: 100px;line-height: 100px;<!DOCTYPE html>…

TimescaleDB 开源时序数据库

文章目录 1.TimescaleDB介绍2.Hypertable 和 chunk3.Hypertable4.Hypertable操作 开源中间件 # TimescaleDBhttps://iothub.org.cn/docs/middleware/ https://iothub.org.cn/docs/middleware/timescale/timescale-summary/1.TimescaleDB介绍 TimescaleDB是基于PostgreSQL数据…

MATH数据集分享

来源: AINLPer公众号&#xff08;每日干货分享&#xff01;&#xff01;&#xff09; 编辑: ShuYini 校稿: ShuYini 时间: 2024-3-10 很多创新性的研究都可能会遇到数学问题&#xff0c;但是这项技能对于计算机来说仍然是个不小的挑战。为了衡量模型在解决数学问题上的表现。UC…

C++11的简单介绍(上)

1.C11简介 在2003年C标准委员会曾经提交了一份技术勘误表(简称TC1)&#xff0c;使得C03这个名字已经取代了C98称为C11之前的最新C标准名称。不过由于C03(TC1)主要是对C98标准中的漏洞进行修复&#xff0c;语言的核心部分则没有改动&#xff0c;因此人们习惯性的把两个标准合并…

Go语言必知必会100问题-20 切片操作实战

前言 有很多gopher将切片的length和capacity混淆&#xff0c;没有彻底理清这两者的区别和联系。理清楚切片的长度和容量这两者的关系&#xff0c;有助于我们合理的对切片进行初始化、通过append追加元素以及进行复制等操作。如果没有深入理解它们&#xff0c;缺少高效操作切片…