diff --git a/kernel/kalloc.c b/kernel/kalloc.c index 0699e7e..1949525 100644 --- a/kernel/kalloc.c +++ b/kernel/kalloc.c @@ -9,6 +9,12 @@ #include "riscv.h" #include "defs.h" +#define NPAGE ((PHYSTOP - KERNBASE) / PGSIZE) +static int refcnt[NPAGE]; +static inline int pa2idx(void *pa) { + return ((uint64)pa - KERNBASE) / PGSIZE; +} + void freerange(void *pa_start, void *pa_end); extern char end[]; // first address after kernel. @@ -23,10 +29,25 @@ struct { struct run *freelist; } kmem; +void incref(void *pa) { + int idx = pa2idx(pa); + refcnt[idx]++; +} +void decref(void *pa) { + int idx = pa2idx(pa); + refcnt[idx]--; +} +int getref(void *pa) { + int idx = pa2idx(pa); + return refcnt[idx]; +} + void kinit() { initlock(&kmem.lock, "kmem"); + for(int i = 0; i < NPAGE; i++) + refcnt[i] = 0; freerange(end, (void*)PHYSTOP); } @@ -51,6 +72,16 @@ kfree(void *pa) if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP) panic("kfree"); + int idx = pa2idx(pa); + acquire(&kmem.lock); + if(refcnt[idx] > 1) { + refcnt[idx]--; + release(&kmem.lock); + return; + } + refcnt[idx] = 0; + release(&kmem.lock); + // Fill with junk to catch dangling refs. memset(pa, 1, PGSIZE); @@ -76,7 +107,10 @@ kalloc(void) kmem.freelist = r->next; release(&kmem.lock); - if(r) + if(r) { memset((char*)r, 5, PGSIZE); // fill with junk + int idx = pa2idx((void*)r); + refcnt[idx] = 1; + } return (void*)r; } diff --git a/kernel/riscv.h b/kernel/riscv.h index f7aaa8a..585f618 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) // 使用RSW位标记COW // shift a physical address to the right place for a PTE. #define PA2PTE(pa) ((((uint64)pa) >> 12) << 10) diff --git a/kernel/trap.c b/kernel/trap.c index d454a7d..946c62b 100644 --- a/kernel/trap.c +++ b/kernel/trap.c @@ -16,6 +16,9 @@ void kernelvec(); extern int devintr(); +typedef uint64 pte_t; +pte_t *walk(pagetable_t pagetable, uint64 va, int alloc); + void trapinit(void) { @@ -50,7 +53,10 @@ usertrap(void) // save user program counter. p->trapframe->epc = r_sepc(); - if(r_scause() == 8){ + uint64 scause = r_scause(); + uint64 stval = r_stval(); + + if(scause == 8){ // system call if(killed(p)) @@ -67,6 +73,23 @@ usertrap(void) syscall(); } else if((which_dev = devintr()) != 0){ // ok + } else if((scause == 15 || scause == 13)) { // store/load page fault + pte_t *pte = walk(p->pagetable, stval, 0); + if(pte && (*pte & PTE_V) && (*pte & PTE_U) && (*pte & PTE_COW)) { + uint64 pa = PTE2PA(*pte); + char *mem = kalloc(); + if(mem == 0) { + setkilled(p); + } else { + memmove(mem, (void*)pa, PGSIZE); + *pte = PA2PTE(mem) | (PTE_FLAGS(*pte) & ~PTE_COW) | PTE_W; + kfree((void*)pa); + } + } else { + printf("usertrap(): unexpected scause 0x%lx pid=%d\n", scause, p->pid); + printf(" sepc=0x%lx stval=0x%lx\n", r_sepc(), r_stval()); + setkilled(p); + } } 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()); @@ -216,3 +239,6 @@ devintr() } } +void kfree(void *pa); +void *kalloc(void); + diff --git a/kernel/vm.c b/kernel/vm.c index 62421a2..ddd6309 100644 --- a/kernel/vm.c +++ b/kernel/vm.c @@ -6,6 +6,10 @@ #include "defs.h" #include "fs.h" +void incref(void *pa); +void kfree(void *pa); +void *kalloc(void); + /* * the kernel's page table. */ @@ -315,7 +319,7 @@ uvmcopy(pagetable_t old, pagetable_t new, uint64 sz) pte_t *pte; uint64 pa, i; uint flags; - char *mem; + // char *mem; for(i = 0; i < sz; i += PGSIZE){ if((pte = walk(old, i, 0)) == 0) @@ -324,19 +328,21 @@ uvmcopy(pagetable_t old, pagetable_t new, uint64 sz) panic("uvmcopy: page not present"); pa = PTE2PA(*pte); 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); - goto err; + // 如果是可写页,去掉PTE_W,设置PTE_COW + if(flags & PTE_W) { + flags = (flags & ~PTE_W) | PTE_COW; + *pte = PA2PTE(pa) | flags; } + // 子页表同样映射,权限同上 + if(mappages(new, i, PGSIZE, pa, flags) != 0){ + // kfree(mem); // 不再分配新页 + uvmunmap(new, 0, i / PGSIZE, 0); + return -1; + } + // 增加物理页引用计数 + incref((void*)pa); } return 0; - - err: - uvmunmap(new, 0, i / PGSIZE, 1); - return -1; } // mark a PTE invalid for user access. @@ -366,9 +372,25 @@ copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len) 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) + if(pte == 0 || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0) return -1; + if((*pte & PTE_W) == 0) { + // COW处理 + if((*pte & PTE_COW) != 0) { + pa0 = PTE2PA(*pte); + char *mem = kalloc(); + if(mem == 0) + return -1; + memmove(mem, (void*)pa0, PGSIZE); + // 更新PTE为新物理页,可写,去掉COW + *pte = PA2PTE(mem) | PTE_FLAGS(*pte) | PTE_W; + *pte &= ~PTE_COW; + // 原物理页引用计数减一 + kfree((void*)pa0); + } else { + return -1; + } + } pa0 = PTE2PA(*pte); n = PGSIZE - (dstva - va0); if(n > len)