1. riscv.h中的修改

#define PTE_RSW (1L << 8)   // 用于cow的页表项标志位
#define GO(addr) (((addr) - KERNBASE) / PGSIZE)
// 把一个物理页映射为一个数组标号,下面的ref_count[GO(addr)]
//表示地址为addr的物理页的引用计数

2. kalloc.c 中的修改

unsigned char ref_count[32*1024];  // 页面的引用计数
struct spinlock ref_lock;    // 包含上面数组的锁void
kfree(void *pa)
{struct run *r;uint64 cou = GO((uint64)pa);acquire(&ref_lock);if(ref_count[cou] >= 2){ref_count[cou]--;release(&ref_lock);return;}ref_count[cou] = 0;release(&ref_lock);if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)panic("kfree");// Fill with junk to catch 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;release(&kmem.lock);if(r){memset((char*)r, 5, PGSIZE); // fill with junkuint64 cou = GO((uint64)r);ref_count[cou]++;}return (void*)r;
}

对kfree的修改就是增加查看需要释放的页面的引用计数这一步,只有ref<=1才会释放该页面。小心,在查看页面的引用计数的时候一定要加锁保护,这里可能有竞争。kalloc就是在分出页面的时候,增加该页面的引用计数,这里不需要加锁,因为该页面目前只分给了这一个进程

3. trap.c的修改

 else if(r_scause() == 15){pte_t *pte = walk(p->pagetable, r_stval(), 0);if(pte == 0 || (*pte & PTE_RSW) == 0){p->killed = 1;printf("%p\n", *pte);printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid);printf("%s,       sepc=%p stval=%p\n",p->name, r_sepc(), r_stval());}else{page_w_fault(pte, p, 0);    }}

在usertrap中额外增加异常号15的判断。page_w_fault用来处理cow的页错误,该函数的实现放在vm.c中。

4. vm.c的修改

这里是改动比较多的地方

//修改了copyout, uvmcopy,增加了page_w_fault函数
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");*pte = (*pte & (~PTE_W)) | PTE_RSW;pa = PTE2PA(*pte);flags = PTE_FLAGS(*pte);/*if((mem = kalloc()) == 0)goto err;memmove(mem, (char*)pa, PGSIZE);*/pte = walk(new, i, 1);if(pte == 0)goto err;*pte = PA2PTE(pa) | flags;acquire(&ref_lock);ref_count[GO((uint64)pa)]++; release(&ref_lock);/*if(mappages(new, i, PGSIZE, (uint64)mem, flags) != 0){kfree(mem);goto err;}*/}return 0;err:uvmunmap(new, 0, i / PGSIZE, 1);return -1;
}

现在的uvmcopy函数,先拿到old_pagetable的pte条目,然后清除掉W位,加上RSW位。传入新页表new,调用walk函数,得到新页表的pte条目,填写方式为需要映射到的物理地址pa 与上 标志位flags。循环结束时就把新页表的表项填好了。注意两点:

  • 新进程和旧进程的页表是独立的,它们页表项占用分别的物理内存,只是页表项把虚拟地址空间映射到同一片物理地址空间(新老进程的第一级,第二级页表条目可能不一样,只有第三级页表条目是一样的)
  • 每次填写好新进程的页表项后,对应物理页面的引用计数加1,这里需要加锁保护
int page_w_fault(pte_t *pte, struct proc* p, int sign){uint64 pa = PTE2PA(*pte);acquire(&ref_lock);if(ref_count[GO(pa)] <= 0){release(&ref_lock);panic("ref error");}if(ref_count[GO(pa)] == 1){*pte |= PTE_W;*pte &= ~PTE_RSW;release(&ref_lock);}else{uint64 paddr = (uint64)kalloc();if(paddr == 0){printf("now more memory\n");if(sign == 1){release(&ref_lock);return -1;}elsep->killed = 1;}else{*pte = PA2PTE(paddr) | PTE_W | PTE_R | PTE_X | PTE_U | PTE_V;memmove((void*)paddr, (void*)pa, PGSIZE);ref_count[GO(pa)]--;}release(&ref_lock);}return 0;
}

有两个地方会调用page_w_fault,一个是在usertrap中,另一个是在copyout中。两者在内存不够的情况处理是不一样的,因此额外用了一个sign标志位。sign为0表示在usertrap中调用,1表示在copyout中调用。

  • 同样的,这里在访问引用计数数组时也需要加锁保护
  • 如果该页面引用计数为1,就不需要额外申请页面了,在PTE条目中加上W位即可。
  • 如果该页面引用计数大于1,就额外申请一个页面,然后修改PTE,使之指向新的页面即可,相应的原页面引用计数减少1(这里记得把页面内容copy过去,之前忘了这件事,debug了半天)。
  • 如果申请页面失败,对于在trap中调用的情况,就把该进程杀死,如果在copyout中调用,就返回-1,表示申请失败
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)return -1;if((*pte & PTE_V) == 0)return -1;if((*pte & PTE_U) == 0)return -1;if(((*pte & PTE_W) == 0) && (*pte & PTE_RSW)){if(page_w_fault(pte, myproc(), 1) == -1){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;
}

copyout原本是调用walkaddr获取对应物理地址,现在改为用walk获取页表条目(这样才能判断是否需要调用page_w_fault函数),顺便把walkaddr中做的检查都搬到copyout中来了。

OK,这个lab就做完了。

6.S081 lab6 cow相关推荐

  1. xv6 6.S081 Lab5: cow

    xv6 6.S081 Lab5: cow 写在前面 实验介绍 开始! cow代码在这里.完成了lazy后,cow的实现就非常明了了-- 写在前面 经典写在前面

  2. 操作系统MIT6.S081:[xv6参考手册第4章]->Trap与系统调用

    本系列文章为MIT6.S081的学习笔记,包含了参考手册.课程.实验三部分的内容,前面的系列文章链接如下 操作系统MIT6.S081:[xv6参考手册第1章]->操作系统接口 操作系统MIT6. ...

  3. MIT 6.s081学习笔记

    MIT 6.s081学习笔记 introduction 计算机组织结构: 最底部是一些硬件资源,包括了CPU,内存,磁盘,网卡 最上层会运行各种应用程序,比如vim,shell等,这些就是正在运行的所 ...

  4. MIT6.S081 2021 Copy-on-Write Fork for xv6

    MIT6.S081 2021 Copy-on-Write Fork for xv6 简要介绍 debug 代码参考 简要介绍 There is a saying in computer systems ...

  5. MIT6.S081简单总结

    在复盘MIT6.828的时候,偶然看到了一个将MIT6.S081的视频翻译成文字的gitbook:https://mit-public-courses-cn-translatio.gitbook.io ...

  6. POJ 3268 D-Silver Cow Party

    http://poj.org/problem?id=3268 Description One cow from each of N farms (1 ≤ N ≤ 1000) conveniently ...

  7. usaco Cow Tours 牛的旅行

    Cow Tours 牛的旅行 农民 John 的农场里有很多牧区.有的路径连接一些特定的牧区.一片所有连通的牧区称为一个牧场. 但是就目前而言,你能看到至少有两个牧区不连通.这样,农民 John 就有 ...

  8. 贪心  POJ - 3617 ​​​​​​​Best Cow Line

    Best Cow Line POJ - 3617 FJ is about to take his N (1 ≤ N ≤ 2,000) cows to the annual"Farmer of ...

  9. 【POJ】3268 Silver Cow Party (将有向图的边反转)

    问题链接:http://poj.org/problem?id=3268 [问题描述] One cow from each of N farms (1 ≤ N ≤ 1000) conveniently ...

最新文章

  1. 坚持刷题678天的感受!
  2. [MySQL]增加用户 授权 远程登录
  3. Salesforce发布人工智能工具分析社交媒体内容
  4. 搞机器学习需要数学基础吗?
  5. IE浏览器网页无法缩放怎么办 解决IE浏览器网页无法缩放的方法
  6. 构造函数与toString
  7. Numpy 之 where理解
  8. 关于sql备份到其他服务器的问题
  9. dbtool一bug跟踪记
  10. 【Computer Organization笔记14】指令流水中的控制冲突和异常
  11. 白色情人节为你身边的程序猿献上一份礼物!
  12. 【NIO系列】——之Netty
  13. 一次Pmbus 调试经验
  14. RS485通信和Modbus通信协议
  15. 【树莓派】基于树莓派的语音机器人
  16. 科大讯飞语音开发包上手体验(1)
  17. 快速搭建静态网站生成器
  18. win10 解决:无法访问网络地址
  19. 西安邮电大学python期末考试_python想过期末考试再怎么学?
  20. 有温度传感器的风机控制系统C语言,基于单片机的暖风机的设计任务书、开题报告...

热门文章

  1. 58同城网站的一些想法
  2. 微信小程序wxml如何判断字符串中汉语某字符_python小课堂23 - 正则表达式(一)
  3. 2004年十月十一日
  4. Matlab 小球落地问题
  5. [内附完整源码和文档] 基于SSH的智博书店系统设计与实现
  6. 机械革命3060 win10 离线安装卸载cuda11.1、cudnn、anaconda pytorch环境全纪录
  7. FreeMarker Thymeleaf - 前端模板引擎
  8. 直接赋值和引用赋值的区别
  9. 关系数据库:专门关系运算
  10. Linux系统编程------mmap函数