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

@@ -60,9 +60,13 @@ void ramdiskintr(void);
void ramdiskrw(struct buf*);
// kalloc.c
#define PLUS 1
#define SUB 0
void* kalloc(void);
void kfree(void *);
void kinit(void);
void reference_operation(void* pa, int op);
void* cowcopy_pa(void* pa);
// log.c
void initlog(int, struct superblock*);
@@ -173,6 +177,8 @@ uint64 walkaddr(pagetable_t, uint64);
int copyout(pagetable_t, uint64, char *, uint64);
int copyin(pagetable_t, char *, uint64, uint64);
int copyinstr(pagetable_t, char *, uint64, uint64);
int iscowpage(uint64 va);
void startcowcopy(uint64 va);
// plic.c
void plicinit(void);

View File

@@ -8,12 +8,23 @@
#include "spinlock.h"
#include "riscv.h"
#include "defs.h"
void freerange(void *pa_start, void *pa_end);
uint64 getRefIdx(uint64 pa);
struct spinlock reference_lock; // 加锁
int pm_reference[(PHYSTOP - KERNBASE)/PGSIZE]; // 记录物理页的引用计数
extern char end[]; // first address after kernel.
// defined by kernel.ld.
// va映射为idx
uint64
getRefIdx(uint64 pa)
{
return (pa-KERNBASE)/PGSIZE;
}
struct run {
struct run *next;
};
@@ -27,6 +38,7 @@ void
kinit()
{
initlock(&kmem.lock, "kmem");
initlock(&reference_lock, "pm_reference");
freerange(end, (void*)PHYSTOP);
}
@@ -51,15 +63,19 @@ kfree(void *pa)
if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)
panic("kfree");
// Fill with junk to catch dangling refs.
memset(pa, 1, PGSIZE);
acquire(&reference_lock);
pm_reference[getRefIdx((uint64)pa)] --;
if(pm_reference[getRefIdx((uint64)pa)] <= 0){
// 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);
}
r = (struct run*)pa;
acquire(&kmem.lock);
r->next = kmem.freelist;
kmem.freelist = r;
release(&kmem.lock);
release(&reference_lock);
}
// Allocate one 4096-byte page of physical memory.
@@ -76,7 +92,43 @@ kalloc(void)
kmem.freelist = r->next;
release(&kmem.lock);
if(r)
if(r){
memset((char*)r, 5, PGSIZE); // fill with junk
pm_reference[getRefIdx((uint64) r)] = 1;
}
return (void*)r;
}
// reference操作
void
reference_operation(void* pa, int op)
{
acquire(&reference_lock);
if(op) pm_reference[getRefIdx((uint64)pa)]++;
else pm_reference[getRefIdx((uint64)pa)]--;
release(&reference_lock);
}
void*
cowcopy_pa(void* pa){
acquire(&reference_lock);
if(pm_reference[getRefIdx((uint64)pa)] <= 1){
release(&reference_lock);
return pa;
}
char* new = kalloc();
if(new == 0){
release(&reference_lock);
panic("out of memory");
return 0;
}
memmove((void*)new, pa, PGSIZE);
// 变更引用计数
pm_reference[getRefIdx((uint64)pa)] --;
release(&reference_lock);
return (void*)new;
}

View File

@@ -1,3 +1,4 @@
#include "spinlock.h"
// Saved registers for kernel context switches.
struct context {
uint64 ra;

View File

@@ -362,6 +362,7 @@ typedef uint64 *pagetable_t; // 512 PTEs
#define PTE_W (1L << 2)
#define PTE_X (1L << 3)
#define PTE_U (1L << 4) // user can access
#define PTE_COW (1L << 8)
// shift a physical address to the right place for a PTE.
#define PA2PTE(pa) ((((uint64)pa) >> 12) << 10)

View File

@@ -1,6 +1,6 @@
// Mutual exclusion lock.
struct spinlock {
uint locked; // Is the lock held?
uint64 locked; // Is the lock held?
// For debugging:
char *name; // Name of lock.

View File

@@ -67,6 +67,8 @@ usertrap(void)
syscall();
} else if((which_dev = devintr()) != 0){
// ok
} else if((r_scause() == 15 || r_scause() == 13) && iscowpage(r_stval())){
startcowcopy(r_stval());
} else {
printf("usertrap(): unexpected scause 0x%lx pid=%d\n", r_scause(), p->pid);
printf(" sepc=0x%lx stval=0x%lx\n", r_sepc(), r_stval());

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;
}

1
time.txt Normal file
View File

@@ -0,0 +1 @@
8