首页 > 其他分享 >xv6 cow

xv6 cow

时间:2023-12-03 18:34:39浏览次数:32  
标签:return cow COW pte xv6 pa PTE 页面

虚拟内存提供了一定程度的间接性:内核可以通过将PTE标记为无效或只读来拦截内存引用,从而导致页面错误,并可以通过修改PTE来更改地址的含义。

xv6中的fork系统调用将父进程的所有用户空间内存复制到子进程中。如果父对象很大,则复制可能需要很长时间。更糟糕的是,这项工作经常被大量浪费:fork通常在子进程后跟exec,这会丢弃复制的内存,其中大部分复制内存都不使用。但如果父级和子级都使用复制的页面,而且都写入,则确实需要复制。

COW fork只为子进程创建一个页表,并且复制父进程的页表。但为了支持父子进程对同一页面的读写,COW fork将页表所有PTE标记为只读。当任一进程试图写入其中一个COW页面时,CPU将强制执行页面故障,从而进入usertrap。usertrap缺页处理程序检测到这种情况(r_scause() = 15,Store page fault),为出错进程分配一页物理内存,将原始页面内容复制到新页面中,并修改出错进程中的对应PTE以引用新页面,标记为可写。当页面错误处理程序返回时,用户进程将能够写入页面的副本,并且重新执行导致缺页的程序。

对于设计COW面临的问题,以下是解决方法:

  1. 如何判断一个页面是不是采用了COW?
    在PTE的RSW保留位中设置新的PTE_COW标志,表示该页面采用了COW。
  2. 多个进程共享同一页面时,如何保证正确保留和释放页面?
    为每个物理页面维护一个引用计数,计数为0时,释放页面。
  3. 如何维护引用计数?
    使用数组存储计数,以pa/PGSIZE作为索引。fork创建子进程共享页面时,计数加1,进程调用kfree时,计数减1。由于共享页面的进程会争用引用计数,因此需要使用锁来保护。

需要修改哪些函数代码?
为了方便操作,重新定义了一些全局变量、辅助函数和宏。锁的使用之前的lab已经有了,这里不再赘述。

// riscv.h
#define PTE_COW (1L << 8) // copy on write
#define PGINDEX(a)     (((a)) >> PGSHIFT)

// kalloc.c
uint pgref[PHYSTOP>>PGSHIFT]; // page reference count
struct spinlock reflock; // lock for pgref
void incref(uint64 pa){
  uint idx = PGINDEX(pa);
  acquire(&reflock);
  pgref[idx]++;
  release(&reflock);
}

void setref(uint64 pa, uint cnt){
  uint idx = PGINDEX(pa);
  acquire(&reflock);
  pgref[idx] = cnt;
  release(&reflock);
}

void decref(uint64 pa){
  uint idx = PGINDEX(pa);
  acquire(&reflock);
  pgref[idx]--;
  release(&reflock);
}

usertrap()

处理Store page fault时,检测COW,分配新的物理页面,将原始页面内容复制到新页面中,并修改出错进程中的对应PTE以引用新页面,标记为可写。
由于类似的行为在copyout中也可能会用到,因此将其封装为一个函数CowHandler。decref设计原本是为了在分配新页面后,递减原页面引用计数,这个任务直接交给kfree,来确保计数为0的页面能被立即释放,而使用decref可能导致页面无法正常释放的问题(three中分配了很多页面,第三次中kalloc会出错,没有剩余空间)。

// trap.c
else if(r_scause() == 15){
    int err = CowHandler(r_stval());
    if(err < 0){
      printf("usertrap(): unexpected CowHandler err %p pid=%d\n", err, p->pid);
      printf("            sepc=%p stval=%p\n", r_sepc(), r_stval());
      setkilled(p);
    }
}

int CowHandler(uint64 va){
  pte_t *pte = walk(myproc()->pagetable, va, 0);
  if(pte == 0 || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0 || (*pte & PTE_COW) == 0)
    return -1;
  acquire(&reflock);
  uint cnt = pgref[PGINDEX(*pte)];
  if(cnt == 1){
    *pte &= ~PTE_COW;
    *pte |= PTE_W;
    release(&reflock);
    return 0;
  }
  release(&reflock);
  char* mem = kalloc();
  if(mem == 0)
    return -2;
  uint64 pa = PTE2PA(*pte);
  memmove(mem, (void*)pa, PGSIZE);

  // !!!!!!!!!!!!!! dont use decref, or may cause kalloc return 0
  // if A finds ref = 2, it will kalloc, before A decref, B find ref = 2, which also kalloc
  //then ref get decreased twice and becomes 0, so it is dead.
  kfree((void*)pa);

  *pte = PA2PTE(mem) | PTE_W | PTE_FLAGS(*pte);
  *pte &= ~PTE_COW;
  return 0;
}
  • uvmcopy():未使用COW时,fork函数调用uvmcopy,对父进程页表中所有有效的页表项,为子进程分配空间并复制;而使用COW时,只需要修改父进程和子进程的页表即可
// vm.c
int
uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
{
  pte_t *pte;
  uint64 pa, i;
  uint flags;

  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);
    if(((*pte) & PTE_W)||((*pte) & PTE_COW)){
      *pte &= (~PTE_W);
      *pte |= PTE_COW;
    }
    flags = PTE_FLAGS(*pte);

    if(mappages(new, i, PGSIZE, pa, flags)!=0){
      goto err;
    }
    incref(pa);
  }
  return 0;
 err:
  uvmunmap(new, 0, i / PGSIZE, 1);
  return -1;
}
  • copyout 该函数将内核数据复制到用户空间,所以也可能出现缺页中断,需要处理COW
int
copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
{
  uint64 n, va0, pa0;
  pte_t *pte;

  while(len > 0){
    va0 = PGROUNDDOWN(dstva);
    if(va0 >= MAXVA)
      return -1;
    pte = walk(pagetable, va0, 0);
    if(pte == 0 || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0)
      return -1;
    if((*pte & PTE_W) == 0){
      if(CowHandler(va0) < 0)
        return -1;
    }
    pa0 = PTE2PA(*pte);
    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;
}

标签:return,cow,COW,pte,xv6,pa,PTE,页面
From: https://www.cnblogs.com/wangerblog/p/17873531.html

相关文章

  • xv6 device driver
    Interruptsanddevicedrivers驱动程序是操作系统中管理特定设备的代码:它配置设备硬件,告诉设备执行操作,处理由此产生的中断,并与可能等待设备I/O的进程进行交互。驱动程序需要与它所管理的设备并发执行并且必须理解设备的硬件接口,编写代码可能很棘手。设备通常可以产生中断,内核......
  • [USACO06DEC] Cow Picnic S
    P2853[USACO06DEC]CowPicnicS逆向思维如果顺着题目走,不大好做。考虑该题要求的是可以供所有奶牛到达的牧场,那么不如从奶牛所在的牧场下手即对每个奶牛所在的牧场\(DFS\),对所有到达点标记。那么显然当一个点的标记等于\(k\)时,说明该牧场是合适的。#include<iostream>......
  • xv6 traps
    traps引入三种类型的事件会导致CPU暂时搁置普通指令的执行,并强制将控制转移给处理事件的特殊代码。系统调用。用户程序执行调用指令要求内核为它做一些事情异常。指令(用户或内核)做了一些非法的事情,例如除以零或使用无效的虚拟地址设备中断。当设备发出需要注意的信号时,例......
  • P9194 [USACO23OPEN] Triples of Cows P 题解
    Description给定一棵初始有\(n\)个点的树。在第\(i\)天,这棵树的第\(i\)个点会被删除,所有与点\(i\)直接相连的点之间都会两两连上一条边。你需要在每次删点发生前,求出满足\((a,b)\)之间有边,\((b,c)\)之间有边且\(a\not=c\)的有序三元组\((a,b,c)\)对数。\(n\leq2......
  • xv6book阅读 chapter1
    xv6book主要研究了xv6如何实现它的类Unix接口,但是其思想和概念不仅仅适用于Unix。任何操作系统都必须将进程多路复用到底层硬件上,相互隔离进程,并提供受控制的进程间通信机制。1了解xv6xv6是一个模仿unix内部设计的操作系统,其提供了unix中对应的部分系统调用。理解xv6对于我们理......
  • 项目管理之项目质量管理MoSCoW(莫斯科)优先级排序法
    项目质量管理是项目管理中至关重要的一环,它贯穿于项目的整个生命周期,包括项目启动、规划、执行、监控和控制。为了确保项目工作的质量,我们需要从多个方面入手,以下是一些关于如何保障项目工作质量管理的内容。项目产品质量检查路径项目准备阶段来自客户的质量期望验收标准与容许偏差......
  • mitos - xv6 for riscv
    参考:code:https://github.com/mit-pdos/xv6-riscvbook:https://pdos.csail.mit.edu/6.828/2021/xv6/book-riscv-rev2.pdfnote:https://mit-public-courses-cn-translatio.gitbook.io/mit6-s081/......
  • [CF283E] Cow Tennis Tournamsan
    CF283E答案即为\(\binom{n}{3}\)减去不合法环数。一个三元环中最多1个点出度为2,所以出度为x的点会造成\(\binom{x}{2}\)个不合法的环。\(\Omicron(nm)\)的做法就是枚举i,判断i与n个点连边是否反向(0,1表示)。然后可以发现对于一段区间[l,r]修改后做贡献的点是......
  • P3119 [USACO15JAN] Grass Cownoisseur G 题解
    分析大概是强连通分量里面最水的一道紫题,不过细节挺多的,做题的时候给蒟蒻震惊到了。题目要求是从\(1\)走到某个点,然后再走回\(1\)号点,中途可逆行一次,问最多能经过几个点。有一个明显的思路是存两个图,一个正图一个反图,正图是为了求\(1\)到各个点的距离,反图是为了求各个点......
  • USACO 2023 US Open Platinum Triples of Cows
    洛谷传送门LOJ传送门hottea.一次删点操作的影响太大了,考虑添加虚点以减小影响(相同的套路在CF1882E2TwoPermutations(HardVersion)也出现过)。具体而言,我们把第\(i\)条边\((u,v)\)变成\((u,n+i),(v,n+i)\)。称编号\(\len\)的点为黑点,编号\(>n\)的点......