题目的要求比较清晰, 即对每个进程在 allocproc()
时分配一个页面, 随后在 proc_pagetable()
中将这个页面映射到宏 USYSCALL
指定的地址中, 最后在 freeproc()
时将这个页面释放.
首先, 由于 allocproc
, proc_pagetable
以及 freeproc
是不同的函数, 因此为了做到 Hint 中所述过程, 在 proc
结构体里添加一个变量用于记录当前进程的加速页面:
struct usyscall *usc;
而后在 allocproc
仿照 trapframe
添加页面的分配和记录:
if((p->usc = (struct usyscall *)kalloc()) == 0){
freeproc(p);
release(&p->lock);
return 0;
}
p->usc->pid = p->pid;
而后在 proc_pagetable
中将分配好的页面映射到对应的虚拟地址上, 这里需要注意的是, 代码需要添加到 TRAPFRAME
映射之后且需要仿照上面的代码添加 mappages
失败时的处理代码:
if (mappages(pagetable, USYSCALL, PGSIZE,
(uint64)(p->usc), PTE_U | PTE_R | PTE_V) < 0){
uvmunmap(pagetable, TRAMPOLINE, 1, 0);
uvmunmap(pagetable, TRAPFRAME, 1, 0);
uvmfree(pagetable, 0);
return 0;
}
最后在 freeproc
中将分配到的页面通过 kfree
释放 (如果成功分配的话). 同时需要注意在 proc_freepagetable
中将映射后的 PTE
释放, 否则会导致 panic("freewalk: leaf")
的错误:
// free a proc structure and the data hanging from it,
// including user pages.
// p->lock must be held.
static void
freeproc(struct proc *p)
{
if(p->trapframe)
kfree((void*)p->trapframe);
p->trapframe = 0;
if(p->usc)
kfree((void *)p->usc);
p->usc = 0;
if(p->pagetable)
proc_freepagetable(p->pagetable, p->sz);
p->pagetable = 0;
p->sz = 0;
p->pid = 0;
p->parent = 0;
p->name[0] = 0;
p->chan = 0;
p->killed = 0;
p->xstate = 0;
p->state = UNUSED;
}
// 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);
}
完成以上步骤后运行 pgtbltest
即可得到该部分的通过结果.
题目要求是给定一个页表地址, 将其各级内容打印出来, 那么遍历给定页表其中的 512 个 PTE
, 如果这个 PTE
的 PTE_V
为真, 则有效, 在这之上, 若其 PTE_R
, PTE_W
, PTE_X
均为假, 则说明该 PTE
指向下一级页表, 反之则说明该 PTE
指向一个物理页面, 对页表的每一级都递归执行这个过程并打印即可.
题目建议的解题步骤如下, 首先在 kernel/vm.c
中声明并实现函数 vmprint(pagetable_t pagetable)
, 而后在 kernel/defs.h
中添加函数的声明便于后续的调用, 最后在 kernel/exec.c
中函数返回前插入 if(p->pid==1) vmprint(p->pagetable)
语句以调用函数.
那么首先实现 vmprint
函数, 由于这个函数是被直接调用的, 在其中执行递归多有不便, 因此将函数的主体提出到其他函数中, 实现如下:
void
printwalk(pte_t addr, int level, int index, char is_pte)
{
pagetable_t pagetable;
if (level == 0) {
printf("page table %p\\n", addr);
pagetable = (pagetable_t)addr;
} else {
for (int i = 0; i < level; ++i)
printf(" ..");
printf("%d: pte %p pa %p\\n", index, addr, PTE2PA(addr));
if (is_pte)
return;
pagetable = (pagetable_t)(PTE2PA(addr));
}
for (int i = 0; i < 512; ++i) {
pte_t pte = pagetable[i];
if ((pte & PTE_V) && (pte & (PTE_R | PTE_W | PTE_X)) == 0)
printwalk(pte, level + 1, i, 0);
else if ((pte & PTE_V))
printwalk(pte, level + 1, i, 1);
}
}
void
vmprint(pagetable_t pagetable)
{
printwalk((pte_t)pagetable, 0, 0, 0);
}
在 kernel/defs.h
中添加声明并在 exec.c
中插入对应语句即可 (不放代码了). 完成步骤后启动 xv6
可以得到和题目一致的结果.