usertests and optional finished
This commit is contained in:
4
Makefile
4
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)
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
};
|
||||
|
||||
|
||||
@ -36,3 +36,4 @@
|
||||
#define SYS_pgpte 33
|
||||
#define SYS_kpgtbl 34
|
||||
#define SYS_pgaccess 35
|
||||
#define SYS_dirtypages 36
|
||||
|
||||
@ -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
|
||||
|
||||
34
kernel/vm.c
34
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;
|
||||
}
|
||||
|
||||
62
user/dirtypagestest.c
Normal file
62
user/dirtypagestest.c
Normal file
@ -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);
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
Reference in New Issue
Block a user