- Print a page table
添加一个打印页表的内核函数,参考递归释放页表的函数freewalk(),
vm.c
static char * prefix[3] = {"..", ".. ..", ".. .. .."};
int
pgtblprint(pagetable_t pagetable, int level) {
if (level > 2) {
return 0;
}
for (int i = 0; i < 512; i++) {
pte_t pte = pagetable[i];
if (pte & PTE_V) {
uint64 pa = PTE2PA(pte);
printf("%s%d: pte %p pa %p\n", prefix[level], i, pte, pa);
if ((pte & (PTE_R|PTE_W|PTE_X)) == 0) {
uint64 child = PTE2PA(pte);
pgtblprint((pagetable_t)child, level + 1);
}
}
}
return 0;
}
int
vmprint(pagetable_t pagetable) {
printf("page table %p\n", pagetable);
return pgtblprint(pagetable, 0);
}
defs.h
int vmprint(pagetable_t pagetable);
exec.c
vmprint(p->pagetable);
测试结果
pte-2.PNG
- A kernel page table per process
每个进程进入内核时,都拥有自己的内核页表
proc.h
进程结构体struct proc结构体增加内核页表字段
pagetable_t kernelpagetable; // kernel page table
vm.c
增加proc_kvminit函数,创建页表,并修改kvminit
void
kvminit()
{
kernel_pagetable = proc_kvminit();
// CLINT
uvmmap(kernel_pagetable, CLINT, CLINT, 0x10000, PTE_R | PTE_W);
}
pagetable_t
proc_kvminit()
{
pagetable_t kernelpagetable = uvmcreate();
if (kernelpagetable == 0) {
return 0;
}
uvmmap(kernelpagetable, UART0, UART0, PGSIZE, PTE_R | PTE_W);
uvmmap(kernelpagetable, VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W);
// uvmmap(kernelpagetable, CLINT, CLINT, 0x10000, PTE_R | PTE_W);
uvmmap(kernelpagetable, PLIC, PLIC, 0x400000, PTE_R | PTE_W);
uvmmap(kernelpagetable, KERNBASE, KERNBASE, (uint64)etext-KERNBASE, PTE_R | PTE_X);
uvmmap(kernelpagetable, (uint64)etext, (uint64)etext, PHYSTOP-(uint64)etext, PTE_R | PTE_W);
uvmmap(kernelpagetable, TRAMPOLINE, (uint64)trampoline, PGSIZE, PTE_R | PTE_X);
return kernelpagetable;
}
void uvmmap(pagetable_t pagetable, uint64 va, uint64 pa, uint64 sz, int perm)
{
if (mappages(pagetable, va, sz, pa, perm)) {
panic("uvmmap");
}
}
创建进程的时候,为进程分配独立的页表,以及内核栈
proc.c
p->kernelpagetable = proc_kvminit();
char *pa = kalloc();
if (pa == 0) {
panic("kalloc");
}
uint64 va = TRAMPOLINE - 2*PGSIZE;
uvmmap(p->kernelpagetable, va, (uint64)pa, PGSIZE, PTE_R | PTE_W);
p->kstack = va;
在调度器调度进程时,切换到该进程对应的内核页表
proc.c
// 切换到进程独立的内核页表
w_satp(MAKE_SATP(p->kernelpagetable));
sfence_vma();
swtch(&c->context, &p->context);
// 切换到全局内核页表
kvminithart();
进程结束时,释放进程独享的页表以及内核栈
proc.c
// 释放进程的内核栈
if (p->kstack) {
pte_t* pte = walk(p->kernelpagetable, p->kstack, 0);
if (pte == 0) {
panic("freeproc: walk");
}
kfree((void*)PTE2PA(*pte));
}
p->kstack = 0;
if (p->kernelpagetable) {
kvm_free_kernelpagetable(p->kernelpagetable);
}
vm.c
void
kvm_free_kernelpagetable(pagetable_t pagetable)
{
for (int i = 0; i < 512; i++) {
pte_t pte = pagetable[i];
if ((pte & PTE_V)) {
pagetable[i] = 0;
if ((pte & (PTE_R|PTE_W|PTE_X)) == 0) {
uint64 child = PTE2PA(pte);
kvm_free_kernelpagetable((pagetable_t)child);
}
}
}
kfree((void*)pagetable);
}
测试结果:
usertest1.PNG