【MIT 6.S081】Lab6: Copy-on-Write Fork for xv6

XV6 Lab4: Copy-on-write Fork xv6 测验 阅读详情

本Lab为xv6添加一个copy on write的功能。
笔者用时约6h(太菜啦 不难但是细节多

概述

xv6中原始fork系统的实现是,当产生一个子进程时,直接把父进程页表中的每一页复制给子进程的页表,这样子做无疑有许多物理空间被浪费,因为并不是每一个空间都会在之后被修改。一个经典的思路就是copy on write,也就是一开始父子进程共享一块物理空间,当某一个物理空间需要被写的时候,再进行复制。
具体的做法就是,当父进程fork出一个子进程的时候,关闭父进程所有页表项的写权限,然后进行复制,复制时父子进程映射到相同的物理地址空间。当代码对没有写权限的页表项进行写操作时,有两种情况,分为内核态与用户态,如果在用户态,则会产生page fault;如果在内核态,则是在copyout函数中;两种情况的处理方式相同,都是为错误的虚拟地址重新分配一页物理内存,具体细节见下文。

Implement copy-on write

在写代码之前最好先把细节都想清楚,不然就会像笔者一样产生一堆bug >_<

由于fork函数中会调用uvmcopy函数对页表进行复制,故首先修改uvmcopy函数,将父进程的物理页映射到子进程中,而不是分配新物理页,并清空父子进程对应PTE中的PTE_W标志。具体代码如下,其中的updateref函数之后会讲。

int
uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
{
  pte_t *pte;
  uint64 pa, i;
  uint flags;
  // char *mem;

  for(i = 0; i < sz; i += PGSIZE){
    if((pte = walk(old, i, 0)) == 0)
      panic("uvmcopy: pte should exist");
    if((*pte & PTE_V) == 0)
      panic("uvmcopy: page not present");
    pa = PTE2PA(*pte);

    // remove the PTE_W flag
    flags = PTE_FLAGS(*pte);
    if ((flags & PTE_W) != 0) {
      flags ^= PTE_W;
      *pte ^= PTE_W;
    }

    updateref((void*) pa, 1);

    // map child's va to parent's pa
    if(mappages(new, i, PGSIZE, pa, flags) != 0){
      kfree((void*) pa);
      goto err;
    }
  }
  return 0;

 err:
  uvmunmap(new, 0, i / PGSIZE, 1);
  return -1;
}

接下来需要修改usertrap函数,以便在page fault出现时进行处理。类似lazy lab,需要先判断是否为page fault,这里只需要处理scause为15的情况(13对应load的page fault,15对应store的)。首先需要判断当前错误的虚拟地址对应的PTE是否是不可写状态(文档里说重新用一个位来标志当前页面是否为cow页,但是在这里我直接用是否不可写来判断了,好像不太严谨但是也能过测试),之后便是分配新的物理页,并复制旧物理页内容到新物理页中,把旧物理页映射删除(这里用uvmunmap是有必要的,后面会讲)。

else if(r_scause() == 15) {
    uint64 va = PGROUNDDOWN(r_stval());
    pte_t* pte = walk(p->pagetable, va, 0);
    uint64 flags = PTE_FLAGS(*pte);

    if (((*pte) & PTE_W) == 0) {
      uint64 pa = (uint64) kalloc();
      if (pa == 0) {
        p->killed = 1;
      } else {
        uint64 oldpa = walkaddr(p->pagetable, va);
        memmove((void*) pa, (void*) oldpa, PGSIZE);
        
        uvmunmap(p->pagetable, va, 1, 1);
        mappages(p->pagetable, va, PGSIZE, pa, flags | PTE_W);
      }
    }
  }

在copyout函数中也差不多,处理内核可能发生的写失败问题(内核只有在这个函数中才会写用户空间),处理代码如下所示。

int
copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
{
  uint64 n, va0, pa0;

  while(len > 0){
    va0 = PGROUNDDOWN(dstva);
    pa0 = walkaddr(pagetable, va0);
    if(pa0 == 0)
      return -1;

    // ======= mycode
    pte_t* pte = walk(pagetable, va0, 0);
    if (((*pte) & PTE_W) == 0) {
      uint64 flags = PTE_FLAGS(*pte);
      uint64 oldpa = pa0;
      pa0 = (uint64) kalloc();
      if (pa0 == 0) return -1;
      memmove((void*) pa0, (void*) oldpa, PGSIZE);

      uvmunmap(pagetable, va0, 1, 1);
      mappages(pagetable, va0, PGSIZE, pa0, flags | PTE_W);
    }
    // =======

    n = PGSIZE - (dstva - va0);
    if(n > len)
      n = len;
    memmove((void *)(pa0 + (dstva - va0)), src, n);

    len -= n;
    src += n;
    dstva = va0 + PGSIZE;
  }
  return 0;
}

最后还有一个重点,就是对于每一页物理页,我们不能在kfree函数调用时就直接把它回收,因为它可能还被其他进程所共享。一种解决方案是,为每一页物理页保留引用该页面的进程数,在kalloc函数中将该物理页的引用数置为1;在父进程fork时也就是uvmcopy函数中;增加物理页的引用计数(也就是之前说的updateref函数);在kfree中,减少该物理页的引用计数(如果为0则不减,一开始会在kinit中调用kfree进行初始化所以会为0),如果减少之后引用计数不为0,则不回收该物理页。
由于该引用计数数组需要被多个CPU共享,于是需要加个自旋锁,定义数据结构如下。其中,数组ref的大小其实只需要(PHYSTOP - end) / PGSIZE。

struct {
  struct spinlock lock;
  int ref[(PHYSTOP-KERNBASE) / PGSIZE];
} kmemref;

然后定义两个函数,方便引用计数的读与修改。其中updateref函数将物理页r的引用计数增加k,getref函数获取某个物理页的引用计数。

void 
updateref(void* r, int k)
{
  acquire(&kmemref.lock);
  kmemref.ref[((uint64)r - KERNBASE) / PGSIZE] += k;
  release(&kmemref.lock);
}

int
getref(void* r) 
{
  acquire(&kmemref.lock);
  int res = kmemref.ref[((uint64)r - KERNBASE) / PGSIZE];
  release(&kmemref.lock);

  return res;
}

最后修改kalloc和kfree函数,如下所示。这里重新解释一下uvmunmap调用的必要性,其实就是利用uvmunmap会调用kfree函数的特点,将对应物理页的引用计数减一,这样就很方便啦。

void
kfree(void *pa)
{
  struct run *r;

  if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)
    panic("kfree");

  // mycode
  if (getref(pa) > 0)
    updateref(pa, -1);
  if (getref(pa) > 0) return;
  // mycode end

  // Fill with junk to catch0 dangling refs.
  memset(pa, 1, PGSIZE);

  r = (struct run*)pa;

  acquire(&kmem.lock);
  r->next = kmem.freelist;
  kmem.freelist = r;
  release(&kmem.lock);
}

void *
kalloc(void)
{
  struct run *r;

  acquire(&kmem.lock);
  r = kmem.freelist;
  if(r) {
    kmem.freelist = r->next;
    // mycode
    updateref((void*)r, 1);
  }
  release(&kmem.lock);

  if(r) {
    memset((char*)r, 5, PGSIZE); // fill with junk
  }
  
  return (void*)r;
}

问题

不知道为啥把uvmcopy改成下面这样就不对呀,有没有大佬浇浇

int
uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
{
  pte_t *pte;
  uint64 pa, i;
  uint flags;
  // char *mem;

  for(i = 0; i < sz; i += PGSIZE){
    if((pte = walk(old, i, 0)) == 0)
      panic("uvmcopy: pte should exist");
    if((*pte & PTE_V) == 0)
      panic("uvmcopy: page not present");
    pa = PTE2PA(*pte);

    // remove the PTE_W flag
    flags = PTE_FLAGS(*pte);
    if ((flags & PTE_W) != 0) {
      flags ^= PTE_W;
      *pte ^= PTE_W;
    }


    // map child's va to parent's pa
    if(mappages(new, i, PGSIZE, pa, flags) != 0){
    // 删了这里
    //   kfree((void*) pa);
      goto err;
    }
    // 改了这里
    updateref((void*) pa, 1);
  }
  return 0;

 err:
  uvmunmap(new, 0, i / PGSIZE, 1);
  return -1;
}
MIT-6.s081-OS lab cow: Copy-on-Write Fork for xv6 本实验仍然是虚拟内存,所以暂且跳过了中间的内容(指lec和课本) 分析 copy on write fork : 在之前的code walk through(https://blog.csdn.net/RedemptionC/article/details/107709618)中提到了,fork的实现里,有一个uvmcopy的调用,作用是将父进程的内存复制到子进程中,这个复制包括申请物理内存,设置pte 但是在下列情况下: 父进程和子进程都不修改某些内存页 fork之后子进程马上调用exec,而 阅读详情

相关推荐

2020 MIT6.s081 Lab: Copy-on-Write Fork for xv6

2020 MIT6.s081 Lab: Copy-on-Write Fork for xv6

sun%moon 687

Lab6: Copy-on-Write Fork for xv6 详解

Lab6: Copy-on-Write Fork for xv6 问题 xv6中的fork()系统调用将父进程的所有用户空间内存复制到子进程中。如果父进程较大,则复制可能需要很长时间。更糟糕的是,这项工作经常造成大量浪费;例如,子进程中的fork()后跟exec()将导致子进程丢弃复制的内存,而其中的大部分可能都从未使用过。另一方面,如果父子进程都使用一个页面,并且其中一个或两个对该页面有写操作,则确实需要复制。 解决方案 copy-on-write (COW) fork()的目标是推迟到子进程实际需要物

qq_53111905的博客 6942

xv6 6.S081 Lab5: cow

xv6 6.S081 Lab4: cow写在前面实验介绍开始! cow代码在这里。完成了lazy后,cow的实现就非常明了了…… 写在前面 经典写在前面????。cow是copy-on-write的缩写(不是母牛┗|`O′|┛ 嗷~~),从字面上来看就是只在要写的时候复制内存。考虑这样一个情况:调用fork()后,子进程是需要复制所有的父进程内存还是说当且仅当子进程或者父进程要写的时候才复制呢?答案显而易见了。这就是cow的核心思想。 按照惯例,这篇博客OS实验xv6 6.S081 开坑中给出了另一些有

不定期更新系统使用、编程小技巧,哔哩哔哩账号:https://space.bilibili.com/399455629,欢迎关注 2666

【MIT6.S081】Lab6: Copy-on-Write Fork for xv6(详细解答版)

实验内容网址:https://xv6.dgs.zone/labs/requirements/lab6.html本实验的代码分支:https://gitee.com/dragonlalala/xv6-labs-2020/tree//关键点: 内存引用计数、usertrap()、页表。

lllllllllljg的博客 1850

MIT6.S081学习总结-lab6:Copy-On-Write Fork

lab6 也是虚拟内存的一种应用,主要实现fork时的写时复制copy-on-write功能。 问题 xv6中的fork()系统调用将所有父进程的用户空间内存复制到子进程中。如果父进程用户空间很大,复制可能需要很长时间。更糟糕的是,这种复制工作经常被浪费;例如,子进程中的fork()后跟exec()将导致子进程丢弃复制的内存,可能根本就没有使用复制来的很大一部分内存。另一方面,如果父类和子类都使用一个页面,而其中一方或双方都编写该页面,则确实需要一个副本。 解决方法 写时拷贝(COW) fork()的目标是

laplacebh的博客 1260

RenderDoc实战:5分钟搞定安卓Vulkan应用抓帧(含常见连接失败排查)

本文详细介绍了如何使用RenderDoc工具对安卓Vulkan应用进行抓帧分析,包括环境配置、完整抓帧流程及常见连接问题的解决方案。通过实战案例和高级技巧,帮助开发者快速定位渲染问题和性能瓶颈,提升移动图形开发效率。

weixin_28318011的博客 281

深圳大学操作系统综合实验一处理机调度

加深对进程调度的直观认识;掌握xv6操作系统中调度代码的编码实现方法;掌握xv6操作系统中信号量的编码实现;可以使用Linux+Qemu仿真环境;修改xv6内核代码实现基本系统调用;修改xv6内核代码实现指定的调度方法;修改xv6内核代码实现进程间同步所需的信号量机制。硬件:桌面PC软件:Linux 或其他操作系统阅读实验辅助材料完成以下操作操作部分:1) 基本实验操作:创建Helloworld演示程序并运行,实现任意功能的系统调用;(20%)2) 调度器实验:(20%)

jannleo的博客 2223

CA(载波聚合)

文章目录CA基本概念1. CA分类2. Pcell、Scell以及Serving CellPcell(Primary Cell)Scell(Secondary Cell)3. Pcell、Scell部署场景场景1场景2场景3场景44. CA的协议控制4.1 Camping/Connetion Establishment:4.2 CC Management/SCell Addition/Release:4.3 Handover/Measurements:4.4 SCell激活 CA基本概念 CA也就是我们中文

你又来看我了,一起学习吧 4万+

【MIT-6.1810】Lab: Copy-on-Write Fork for xv6

COW 的主要原理如下。当父进程 fork 出子进程时,子进程建立自己的页表,但是不会复制父进程的物理页,而是将每一个 PTE 都指向父进程的物理页,即两大页表同时指向同一片物理内存。而当某一个进程需要对物理页进行写时(可以是父也可以是子),再开辟一个新的物理页,内容为复制原有的物理页,使进程的 PTE 由原来的物理页指向这个新的物理页。

weixin_46322986的博客 1357

wsl2上运行带有Isaac Gym的程序报错: GPU Pipeline: disabled

显示GPU Pipeline:disabled,查找相关文档发现是WSL2目前不支持Isaac Gym,时,想要在wsl2上运行,最后报错如下。

2303_78660611的博客 957

ZYNQ开发(2)在Linux系统层上PL-PS通过AXI-DMA传输数据详细步骤及注意事项

参考的网络资料 https://github.com/bperez77/xilinx_axidma/tree/master https://gitlab.gnome.org/HosseinBeheshti/UlteraZed7EV/-/tree/master/petalinux/xilinx_axidma https://www.icode9.com/content-3-925319.html https://blog.csdn.net/weixin_38218267/article/details/1

aizaiyueye的专栏 3448

MIT 6s081 lab6:Copy-on-Write Fork for xv6

实现 fork 懒复制机制,在进程 fork 后,不立刻复制内存页,而是将虚拟地址指向与父进程相同的物理地址。在父子任意一方尝试对内存页进行修改时,才对内存页进行复制。物理内存页必须保证在所有引用都消失后才能被释放,这里需要有引用计数机制。一开始做的时候没有上锁,一直没通过全部测试。后来参考了下面这位大佬的博客后意识到了竞争问题,加入自旋锁后顺利通过测试。

厚积而薄发 1181

Manus创始人深度复盘:AI Agent产品的技术架构与战略转型

成本对比:同样任务处理,中国区单位成本是亚太区的2.3倍。付费转化率:中国区1.2% vs 全球平均4.7%微信/支付宝等超级App的适配代码占比达17%Zstandard:压缩比38%,耗时12ms。每季度需要投入3名高级工程师专职维护兼容性。国际版API平均延迟:180-220ms。中国版特殊部署延迟:480-720ms。信息保持准确率:93%(7天跨度测试)自研算法:压缩比39%,耗时5ms。存储效率:相比纯文本节省68%空间。:本地化部署导致的延迟与费用差异。LZ4:压缩比42%,耗时8ms。

m0_75253143的博客 7146

[MIT 6.S081] Lab 6: Copy-on-Write Fork for xv6

Lab 6: Copy-on-Write Fork for xv6 Lab Guidance: https://pdos.csail.mit.edu/6.828/2020/labs/cow.html Lab Code: https://github.com/VastRock-Huang/xv6-labs-2020/tree/cow Implement copy-on write(hard) 要点 修改 uvmcopy(), usertrap(), copyout() 等函数 对物理页记录引用数(ref

LostUnravel的博客 5229

PostgreSQL面试题及详细答案120道(101-120)

本文针对PostgreSQL数据库提供全面的性能优化方案,涵盖10个关键面试题解析。主要内容包括:1)并发参数调优(max_connections、worker_processes);2)缓存设置(effective_cache_size);3)WAL日志优化;4)慢查询监控与日志配置;5)会话管理与阻塞处理;6)高写入场景优化策略;7)存储类型参数调整;8)自动清理机制配置;9)大表DDL操作避坑指南;10)性能瓶颈诊断方法论。对DBA和开发者具有实操指导价值

大剑师兰特的GIS世界 855

RT-DETR改进策略【损失函数篇】| 通过辅助边界框计算IoU提升检测效果(Inner_GIoU、Inner_DIoU、Inner_CIoU、Inner_EIoU、Inner_SIoU)

为了弥补现有 IoU 损失在不同检测任务中泛化性弱和收敛速度慢的问题,·Inner-IoU·通过引入比例因子 “ratio” 来控制辅助边界框的尺度大小,利用不同尺度的辅助边界框来计算损失,从而加速边界框回归过程。

Limiiiing的博客 1963

MIT 6.S081 Lab6: Copy-on-Write Fork for xv6

这次的实验与上一个实验(懒分配)很相似,实现写时复制,实验地址在这里。与以往不同的是,这次只有一个任务,虽然是 hard 难度,但写起来并没有很吃力,应该是我写的最快的一个实验了。思路很简单,主要是容易出现各种 bug,debug 的过程会比较煎熬,。我的代码在 github 上。

落队的飞行兵 1824

Android 12 大屏时底下显示任务栏以及虚拟按键靠右问题

Android 12 点大屏时,底下导航栏变成显示任务栏,虚拟按键靠右的分析以及修改

Android系统开发笔记 4686

MIT6.S081 Lab5: Copy-on-Write Fork for xv6

Lab6只有一个实验,目的是为了实现写时复制。写时复制(Copy-on-write,简称COW)是一种计算机程序设计领域的优化策略。其核心思想是,如果有多个调用者(callers)同时请求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变。这过程对其他的调用者都是透明的。

weixin_43724444的博客 796

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

MIT6.S081Lab6:Copy on Write Fork for xv6

实验不难,但是要仔细,而且很多时候,当你觉得你的想法实现太复杂了,多半是想法有问题。

qq_46514141的博客 811

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

MIT 6.S081---Lab: Copy-on-Write Fork for xv6

Implement copy-on write(hard) 从下图可见,xv6的pte中RSW都是可以被软件使用的未定义位,此处将pte中的第9位标记为PTE_COW位。 修改kernel/riscv.h: 修改kernel/vm.c,新增refCount代表物理页面的引用次数,根据(PHYSTOP-KERNBASE)/ PGSIZE得32768: 修改kernel/vm.c中的uvmcopy(): 修改kernel/vm.c中的mmappages(),当页表绑定物理页面时增加引用计数: 修改k

weixin_43917399的博客 510

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

MIT 6.S081 Copy-on-Write Fork for xv6 实验篇(lab6):Implement copy-on write(hard)

Lab: Copy-on-Write Fork for xv6

2603_95068735的博客 421

Mit6.S081-实验6-Copy-on-Write Fork for xv6

Mit6.S081-实验6-Copy-on-Write Fork for xv6一、前言二、The problem三、The solution四、Implement copy-on write1,实验要求2,进攻计划3,一些提示4,具体实现5,执行效果 一、前言 虚拟内存提供一定程度的重定向:kernel可以通过标记PTEs无效、只读(导致page faults)来中断内存引用; kernel也可以通过改变地址含义(通过更改PTES)。 在电脑系统中有个说法:系统问题可以通过一定程度的重定向解决。 lazy

code a better life~ 33万+

MIT6.S081 - Lab6 Copy-on-Write(写时复制)

本篇是 MIT6.S081 操作系统课程 Lab6 的实验笔记,目标是完成fork()写时复制(Copy-on-Write)的功能,算是上一个懒分配实验的延伸。

1294

操作系统MIT6.S081:Lab6->Copy-on-write fork

本系列文章为MIT6.S081的学习笔记,包含了参考手册、课程、实验三部分的内容,前面的系列文章链接如下 操作系统MIT6.S081:[xv6参考手册第1章]->操作系统接口 操作系统MIT6.S081:[xv6参考手册第2章]->操作系统组织结构 操作系统MIT6.S081:[xv6参考手册第3章]->页表 操作系统MIT6.S081:[xv6参考手册第4章]->Trap与系统调用 操作系统MIT6.S081:P1->Introduction and examples 操作系统MIT6.S081:P2->

InnerPeaceHQ的博客 1452

MIT 6.S081 Lab6:Copy-on-Write Fork

本文介绍了xv6系统中实现写时复制(Copy-on-Write)机制的关键步骤。主要包含:1)在fork时标记共享页为COW页并设为只读;2)处理页错误时分配新物理页并修改权限;3)引入引用计数管理共享页,确保安全释放;4)处理内核态对用户COW页的写操作。该机制通过延迟物理页分配,显著提升了fork效率并节省内存空间。文章还讨论了页表抽象带来的扩展性,如支持懒分配、按需分页等功能。实验涉及内核页表处理、锁机制和内存管理等核心概念。

gcfer的博客 656
上一篇: 【MIT 6.S081】Lab5: xv6 lazy page allocation
下一篇: 【MIT 6.S081】Lab7: Multithreading
juruo_c
博客等级 码龄6年 520粉丝 116原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值