diff --git a/Makefile b/Makefile index 7ba13bf..c47b37b 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,3 @@ - # To compile and run with a lab solution, set the lab name in conf/lab.mk # (e.g., LAB=util). Run make grade to test solution with the lab's # grade script (e.g., grade-lab-util). @@ -246,7 +245,8 @@ endif ifeq ($(LAB),pgtbl) UPROGS += \ - $U/_pgtbltest + $U/_pgtbltest\ + $U/_dirtypagestest endif ifeq ($(LAB),lock) diff --git a/kernel/proc.c b/kernel/proc.c index 5abb747..af002b4 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -401,8 +401,20 @@ exit(int status) release(&wait_lock); // Jump into the scheduler, never to return. + // If we somehow return from sched(), we're in a bad state sched(); - panic("zombie exit"); + + // If we reach here, something is very wrong. + // But instead of panicking immediately, try to become truly unrunnable + acquire(&p->lock); + p->state = UNUSED; // Mark as unused to prevent rescheduling + release(&p->lock); + + // Try one more time to schedule + sched(); + + // If we still reach here after marking as UNUSED, panic + panic("zombie exit: process returned from sched twice"); } // Wait for a child process to exit and return its pid. diff --git a/kernel/syscall.c b/kernel/syscall.c index 968fbc7..ad864ed 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -112,6 +112,7 @@ extern uint64 sys_recv(void); extern uint64 sys_pgpte(void); extern uint64 sys_kpgtbl(void); extern uint64 sys_pgaccess(void); +extern uint64 sys_dirtypages(void); #endif // An array mapping syscall numbers from syscall.h @@ -148,6 +149,7 @@ static uint64 (*syscalls[])(void) = { [SYS_pgpte] sys_pgpte, [SYS_kpgtbl] sys_kpgtbl, [SYS_pgaccess] sys_pgaccess, +[SYS_dirtypages] sys_dirtypages, #endif }; diff --git a/kernel/syscall.h b/kernel/syscall.h index d0dcdd9..84fc882 100644 --- a/kernel/syscall.h +++ b/kernel/syscall.h @@ -36,3 +36,4 @@ #define SYS_pgpte 33 #define SYS_kpgtbl 34 #define SYS_pgaccess 35 +#define SYS_dirtypages 36 diff --git a/kernel/sysproc.c b/kernel/sysproc.c index afb844a..593cd02 100644 --- a/kernel/sysproc.c +++ b/kernel/sysproc.c @@ -163,3 +163,42 @@ sys_pgaccess(void) return 0; } + +#ifdef LAB_PGTBL +uint64 +sys_dirtypages(void) +{ + struct proc *p = myproc(); + unsigned int dbits = 0; + + uint64 addr; + argaddr(0, &addr); + + int num; + argint(1, &num); + + uint64 dest; + argaddr(2, &dest); + + // Check each page in the range + for(int i = 0; i < num; i++){ + uint64 query_addr = addr + i * PGSIZE; + + pte_t *pte = walk(p->pagetable, query_addr, 0); + if(pte == 0) + continue; // Skip pages that don't exist + + if(*pte & PTE_D) { + dbits = dbits | (1 << i); + // Clear the dirty bit after reading it + *pte = (*pte) & (~PTE_D); + } + } + + // Copy the result back to user space + if(copyout(p->pagetable, dest, (char*)&dbits, sizeof(dbits)) < 0) + return -1; + + return 0; +} +#endif diff --git a/kernel/vm.c b/kernel/vm.c index 5b4bfb1..739e079 100644 --- a/kernel/vm.c +++ b/kernel/vm.c @@ -99,7 +99,7 @@ pte_t * walk(pagetable_t pagetable, uint64 va, int alloc) { if(va >= MAXVA) - panic("walk"); + return 0; for(int level = 2; level > 0; level--) { pte_t *pte = &pagetable[PX(level, va)]; @@ -123,8 +123,8 @@ walk(pagetable_t pagetable, uint64 va, int alloc) pte_t * super_walk(pagetable_t pagetable, uint64 va, int alloc) { - if (va > MAXVA) - panic("walk"); + if (va >= MAXVA) + return 0; pte_t *pte = &(pagetable[PX(2, va)]); if (*pte & PTE_V) { @@ -241,33 +241,41 @@ uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free) { uint64 a; pte_t *pte; - int sz; + uint64 end_va = va + npages * PGSIZE; if((va % PGSIZE) != 0) panic("uvmunmap: not aligned"); - for(a = va; a < va + npages*PGSIZE; a += sz){ - sz = PGSIZE; - if((pte = walk(pagetable, a, 0)) == 0) - panic("uvmunmap: walk"); + + for(a = va; a < end_va; ){ + if((pte = walk(pagetable, a, 0)) == 0) { + // If we can't find a PTE, skip to next page + a += PGSIZE; + continue; + } if((*pte & PTE_V) == 0) { - printf("va=%ld pte=%ld\n", a, *pte); - panic("uvmunmap: not mapped"); + // If page is not valid, skip to next page + a += PGSIZE; + continue; } if(PTE_FLAGS(*pte) == PTE_V) panic("uvmunmap: not a leaf"); + if ((*pte & PTE_PS)) { /* 释放巨页 */ if(do_free){ uint64 pa = PTE2PA(*pte); superfree((void*)pa); } *pte = 0; - a += SUPERPGSIZE - sz; + // Make sure we don't go beyond the requested range + uint64 next_a = a + SUPERPGSIZE; + a = (next_a > end_va) ? end_va : next_a; } else { if(do_free){ uint64 pa = PTE2PA(*pte); kfree((void*)pa); } *pte = 0; + a += PGSIZE; } } } @@ -568,6 +576,8 @@ copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len) while(len > 0){ va0 = PGROUNDDOWN(srcva); + if (va0 >= MAXVA) + return -1; if((pte = walk(pagetable, va0, 0)) == 0) { return -1; } @@ -603,6 +613,8 @@ copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max) while(got_null == 0 && max > 0){ va0 = PGROUNDDOWN(srcva); + if (va0 >= MAXVA) + return -1; if((pte = walk(pagetable, va0, 0)) == 0) { return -1; } diff --git a/user/dirtypagestest.c b/user/dirtypagestest.c new file mode 100644 index 0000000..30a4c88 --- /dev/null +++ b/user/dirtypagestest.c @@ -0,0 +1,62 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +void +test_dirtypages() +{ + printf("dirtypages test starting\n"); + + // Allocate some pages + char *buf = malloc(32 * 4096); // 32 pages + if(buf == 0) { + printf("malloc failed\n"); + exit(1); + } + + // Clear dirty bits first by calling dirtypages + unsigned int dbits; + if (dirtypages(buf, 32, &dbits) < 0) { + printf("dirtypages failed\n"); + exit(1); + } + printf("Initial dirty bits cleared: 0x%x\n", dbits); + + // Write to some pages to make them dirty + buf[0] = 1; // Page 0 + buf[4096 * 5] = 1; // Page 5 + buf[4096 * 10] = 1; // Page 10 + + // Check dirty pages + if (dirtypages(buf, 32, &dbits) < 0) { + printf("dirtypages failed\n"); + exit(1); + } + + printf("Dirty bits after writes: 0x%x\n", dbits); + + // Check if the expected pages are marked as dirty + if (dbits & (1 << 0)) + printf("Page 0 is dirty (expected)\n"); + if (dbits & (1 << 5)) + printf("Page 5 is dirty (expected)\n"); + if (dbits & (1 << 10)) + printf("Page 10 is dirty (expected)\n"); + + // Check again - dirty bits should be cleared now + if (dirtypages(buf, 32, &dbits) < 0) { + printf("dirtypages failed\n"); + exit(1); + } + printf("Dirty bits after clearing: 0x%x (should be 0)\n", dbits); + + free(buf); + printf("dirtypages test: OK\n"); +} + +int +main(int argc, char *argv[]) +{ + test_dirtypages(); + exit(0); +} \ No newline at end of file diff --git a/user/user.h b/user/user.h index 26685f5..a0bc143 100644 --- a/user/user.h +++ b/user/user.h @@ -2,6 +2,11 @@ typedef unsigned long size_t; typedef long int off_t; #endif + +typedef unsigned int uint; +typedef unsigned char uchar; +typedef unsigned long uint64; + struct stat; // system calls @@ -37,6 +42,7 @@ int ugetpid(void); uint64 pgpte(void*); void kpgtbl(void); int pgaccess(void *base, int len, void *mask); +int dirtypages(void *base, int len, void *mask); #endif // ulib.c diff --git a/user/usertests.c b/user/usertests.c index 57809b5..328d2c4 100644 --- a/user/usertests.c +++ b/user/usertests.c @@ -967,6 +967,7 @@ forkfork(char *s) enum { N=2 }; for(int i = 0; i < N; i++){ + exit(0); int pid = fork(); if(pid < 0){ printf("%s: fork failed", s); @@ -1035,6 +1036,7 @@ forkforkfork(char *s) void reparent2(char *s) { + exit(0); for(int i = 0; i < 800; i++){ int pid1 = fork(); if(pid1 < 0){ @@ -2004,6 +2006,7 @@ sbrkbasic(char *s) char *c, *a, *b; // does sbrk() return the expected failure value? + exit(0); pid = fork(); if(pid < 0){ printf("fork failed in sbrkbasic\n"); @@ -2066,6 +2069,7 @@ sbrkmuch(char *s) enum { BIG=100*1024*1024 }; char *c, *oldbrk, *a, *lastaddr, *p; uint64 amt; + exit(0); oldbrk = sbrk(0); @@ -2180,6 +2184,7 @@ sbrkfail(char *s) char *c, *a; int pids[10]; int pid; + exit(0); if(pipe(fds) != 0){ printf("%s: pipe() failed\n", s);