From bef91a3bf039f406f6253e9cb38bba3e16d2adf5 Mon Sep 17 00:00:00 2001 From: CGH0S7 <776459475@qq.com> Date: Tue, 1 Jul 2025 20:31:29 +0800 Subject: [PATCH] cowtest passed --- kernel/defs.h | 6 +++ kernel/kalloc.c | 72 +++++++++++++++++++++++---- kernel/proc.h | 1 + kernel/riscv.h | 1 + kernel/spinlock.h | 2 +- kernel/trap.c | 2 + kernel/vm.c | 123 ++++++++++++++++++++++++++++++++-------------- time.txt | 1 + 8 files changed, 161 insertions(+), 47 deletions(-) create mode 100644 time.txt diff --git a/kernel/defs.h b/kernel/defs.h index d1b6bb9..90fa90f 100644 --- a/kernel/defs.h +++ b/kernel/defs.h @@ -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); diff --git a/kernel/kalloc.c b/kernel/kalloc.c index 0699e7e..27efc98 100644 --- a/kernel/kalloc.c +++ b/kernel/kalloc.c @@ -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; +} + diff --git a/kernel/proc.h b/kernel/proc.h index d021857..20d2b32 100644 --- a/kernel/proc.h +++ b/kernel/proc.h @@ -1,3 +1,4 @@ +#include "spinlock.h" // Saved registers for kernel context switches. struct context { uint64 ra; diff --git a/kernel/riscv.h b/kernel/riscv.h index f7aaa8a..d56852a 100644 --- a/kernel/riscv.h +++ b/kernel/riscv.h @@ -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) diff --git a/kernel/spinlock.h b/kernel/spinlock.h index 4392820..768f728 100644 --- a/kernel/spinlock.h +++ b/kernel/spinlock.h @@ -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. diff --git a/kernel/trap.c b/kernel/trap.c index d454a7d..aa6012b 100644 --- a/kernel/trap.c +++ b/kernel/trap.c @@ -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()); diff --git a/kernel/vm.c b/kernel/vm.c index 62421a2..81c35ac 100644 --- a/kernel/vm.c +++ b/kernel/vm.c @@ -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; +} diff --git a/time.txt b/time.txt new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/time.txt @@ -0,0 +1 @@ +8