When each process is created, map one read-only page at USYSCALL (a virtual address defined in memlayout.h). At the start of this page, store a struct usyscall (also defined in memlayout.h), and initialize it to store the PID of the current process. For this lab, ugetpid() has been provided on the userspace side and will automatically use the USYSCALL mapping. You will receive full credit for this part of the lab if the ugetpid test case passes when running pgtbltest.
// An empty user page table. p->pagetable = proc_pagetable(p); if(p->pagetable == 0){ freeproc(p); release(&p->lock); return 0; }
// Set up new context to start executing at forkret, // which returns to user space. memset(&p->context, 0, sizeof(p->context)); p->context.ra = (uint64)forkret; p->context.sp = p->kstack + PGSIZE;
// Create a user page table for a given process, with no user memory, // but with trampoline and trapframe pages. pagetable_t proc_pagetable(struct proc *p) { ... if(mappages(pagetable, TRAPFRAME, PGSIZE, (uint64)(p->trapframe), PTE_R | PTE_W) < 0){ uvmunmap(pagetable, TRAMPOLINE, 1, 0); uvmfree(pagetable, 0); return 0; }
// map the usyscall page below the trapframe. // user program can read it, but not write it. if(mappages(pagetable, USYSCALL, PGSIZE, (uint64)(p->usyscall), PTE_R | PTE_U) < 0){ uvmunmap(pagetable, TRAPFRAME, 1, 0); uvmunmap(pagetable, TRAMPOLINE, 1, 0); uvmfree(pagetable, 0); return 0; }
return pagetable; }
// Free a process's page table, and free the // physical memory it refers to. void proc_freepagetable(pagetable_t pagetable, uint64 sz) { uvmunmap(pagetable, TRAMPOLINE, 1, 0); uvmunmap(pagetable, TRAPFRAME, 1, 0); uvmunmap(pagetable, USYSCALL, 1, 0); uvmfree(pagetable, sz); }
问题设置的也比较有趣: Which other xv6 system call(s) could be made faster using this shared page? Explain how. 这是我的回答:
1 2 3 4
Any system call need to read unsensitive data from kernel space to user space could be made faster using the shared page. This would reduce the overhead of copying data between user and kernel space. For example, the pgaccess system call need to copy mask from kernel to user space, so it could added into the defination of usyscall, stored in the shared page USYSCALL.
Print a page table
Define a function called vmprint(). It should take a pagetable_t argument, and print that pagetable in the format described below. Insert if(p->pid==1) vmprint(p->pagetable) in exec.c just before the return argc, to print the first process’s page table. You receive full credit for this part of the lab if you pass the pte printout test of make grade.
这部分比较无聊,懒得写了。
Detect which pages have been accessed
Your job is to implement pgaccess(), a system call that reports which pages have been accessed. The system call takes three arguments. First, it takes the starting virtual address of the first user page to check. Second, it takes the number of pages to check. Finally, it takes a user address to a buffer to store the results into a bitmask (a datastructure that uses one bit per page and where the first page corresponds to the least significant bit). You will receive full credit for this part of the lab if the pgaccess test case passes when running pgtbltest.
// Inspect the access bits for n pages from virtual address(n <= 32) // Return the mask of access bits while clearing the access bits uint checkaccess(pagetable_t pagetable, uint64 addr, int num) { if (num > 32) panic("pgaccess: num > 32");
uint mask = 0;
for(int i = 0; i < num; i++) { pte_t *pte = walk(pagetable, addr + i * PGSIZE, 0);
if (*pte) { mask |= (*pte & PTE_A) ? (1 << i) : 0; *pte &= ~PTE_A; // clear the access bit } }
return mask; }
感觉…没什么难点(不如编译原理实验),可能是过太久时间忘了,看来 blog 还得写完实验立即写,但实验实在是太多了(哭)。