cowtest passed

This commit is contained in:
2025-07-01 20:31:29 +08:00
parent bc8921a77f
commit bef91a3bf0
8 changed files with 161 additions and 47 deletions

View File

@@ -5,6 +5,8 @@
#include "riscv.h"
#include "defs.h"
#include "fs.h"
#include "proc.h"
#include "spinlock.h"
/*
* the kernel's page table.
@@ -309,13 +311,14 @@ uvmfree(pagetable_t pagetable, uint64 sz)
// physical memory.
// returns 0 on success, -1 on failure.
// frees any allocated pages on failure.
// 如果父进程的一个 PTE 本身就是不可写的则没有必要PTE_COW
// 因为子进程对这块物理页也一定是不可写的,那么就没必要复制,因此只有当父进程的 PTE_W 标记时,才去附加 PTE_COW。
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)
@@ -323,14 +326,20 @@ uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
if((*pte & PTE_V) == 0)
panic("uvmcopy: page not present");
pa = PTE2PA(*pte);
// lab5: Copy on write
// father
// 如果该页本身就不可写那么子进程肯定也不可写不用对其考虑COW
if(*pte & PTE_W){
*pte &= ~PTE_W;
*pte |= PTE_COW;
}
flags = PTE_FLAGS(*pte);
if((mem = kalloc()) == 0)
goto err;
memmove(mem, (char*)pa, PGSIZE);
if(mappages(new, i, PGSIZE, (uint64)mem, flags) != 0){
kfree(mem);
// child
if(mappages(new, i, PGSIZE, (uint64)pa, flags) != 0){
goto err;
}
reference_operation((void*)pa, PLUS);
}
return 0;
@@ -338,7 +347,6 @@ uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
uvmunmap(new, 0, i / PGSIZE, 1);
return -1;
}
// mark a PTE invalid for user access.
// used by exec for the user stack guard page.
void
@@ -352,35 +360,6 @@ uvmclear(pagetable_t pagetable, uint64 va)
*pte &= ~PTE_U;
}
// Copy from kernel to user.
// Copy len bytes from src to virtual address dstva in a given page table.
// Return 0 on success, -1 on error.
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 ||
(*pte & PTE_W) == 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;
}
// Copy from user to kernel.
// Copy len bytes to dst from virtual address srcva in a given page table.
@@ -449,3 +428,75 @@ copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
return -1;
}
}
// 检查是否为cowpage
int
iscowpage(uint64 va){
struct proc* p = myproc();
va = PGROUNDDOWN((uint64)va);
if(va >= MAXVA) // 要在walk之前
return 0;
pte_t* pte = walk(p->pagetable,va,0);
if(pte == 0)
return 0;
if((va < p->sz)&& (*pte & PTE_COW) && (*pte & PTE_V))
return 1;
else
return 0;
}
// cow 的主要操作,将该 PTE 重新映射至新的物理内存并修改flag
void
startcowcopy(uint64 va){
struct proc* p = myproc();
va = PGROUNDDOWN((uint64)va);
pte_t* pte = walk(p->pagetable,va,0);
uint64 pa = PTE2PA(*pte);
void* new = cowcopy_pa((void*)pa);
if((uint64)new == 0){
panic("cowcopy_pa err\n");
exit(-1);
}
uint64 flags = (PTE_FLAGS(*pte) | PTE_W) & (~PTE_COW);
// 确保PTE_U没有丢失
if (!(flags & PTE_U)) flags |= PTE_U;
uvmunmap(p->pagetable, va, 1, 0); // 不包含kfree因为ref--在cowcopy_pa中已经进行了
if(mappages(p->pagetable, va, PGSIZE, (uint64)new, flags) == -1){
kfree(new);
panic("cow mappages failed");
}
}
// Copy from kernel to user.
// Copy len bytes from src to virtual address dstva in a given page table.
// Return 0 on success, -1 on error.
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;
if(iscowpage(va0)){
startcowcopy(va0);
pa0 = walkaddr(pagetable, va0);
}
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;
}