A kernel page table per process (hard)
根据提示一步一步走
在struct proc中为进程的内核页表增加一个字段
diff --git a/kernel/proc.h b/kernel/proc.h
index 9c16ea7..481118f 100644
--- a/kernel/proc.h
+++ b/kernel/proc.h
@@ -98,6 +98,7 @@ struct proc {
uint64 kstack; // Virtual address of kernel stack
uint64 sz; // Size of process memory (bytes)
pagetable_t pagetable; // User page table
pagetable_t prockpt; // 进程的内核页表
struct trapframe *trapframe; // data page for trampoline.S
struct context context; // swtch() here to run process
struct file *ofile[NOFILE]; // Open files
实现一个修改版的kvminit,你将会考虑在allocproc中调用这个函数
原先的内核映射kvmmap写成的是固定参数, 只为kernel_pagetable作映射
添加一个功能一样的uvmmap函数,能传入不同的页表作为参数,不再是固定
diff --git a/kernel/vm.c b/kernel/vm.c
index 8137b05..0984442 100644
--- a/kernel/vm.c
+++ b/kernel/vm.c
@@ -121,6 +161,13 @@ kvmmap(uint64 va, uint64 pa, uint64 sz, int perm)
panic("kvmmap");
}
void
uvmmap(pagetable_t pagetable, uint64 va, uint64 pa, uint64 sz, int perm)
{
if(mappages(pagetable, va, sz, pa, perm) != 0)
panic("uvmmap");
}
// translate a kernel virtual address to
// a physical address. only needed for
// addresses on the stack.
@@ -47,6 +49,37 @@ kvminit()
kvmmap(TRAMPOLINE, (uint64)trampoline, PGSIZE, PTE_R | PTE_X);
}
pagetable_t
prockptinit()
{
pagetable_t prockpt = uvmcreate();
if(prockpt == 0) return 0; //空指针
// uart registers
uvmmap(prockpt, UART0, UART0, PGSIZE, PTE_R | PTE_W);
// virtio mmio disk interface
uvmmap(prockpt, VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W);
// CLINT
uvmmap(prockpt, CLINT, CLINT, 0x10000, PTE_R | PTE_W);
// PLIC
uvmmap(prockpt, PLIC, PLIC, 0x400000, PTE_R | PTE_W);
// map kernel text executable and read-only.
uvmmap(prockpt, KERNBASE, KERNBASE, (uint64)etext-KERNBASE, PTE_R | PTE_X);
// map kernel data and the physical RAM we'll make use of.
uvmmap(prockpt, (uint64)etext, (uint64)etext, PHYSTOP-(uint64)etext, PTE_R | PTE_W);
// map the trampoline for trap entry/exit to
// the highest virtual address in the kernel.
uvmmap(prockpt, TRAMPOLINE, (uint64)trampoline, PGSIZE, PTE_R | PTE_X);
return prockpt;
}
// Switch h/w page table register to the kernel's page table,
// and enable paging.
void
初始化一个进程的内核页表
确保每一个进程的内核页表都关于该进程的内核栈有一个映射。
在未修改的XV6中,所有的内核栈都在procinit中设置。
把这个功能部分或全部的迁移到allocproc中
diff --git a/kernel/proc.c b/kernel/proc.c
index dab1e1d..ad394df 100644
--- a/kernel/proc.c
+++ b/kernel/proc.c
@@ -34,14 +34,14 @@ procinit(void)
// Allocate a page for the process's kernel stack.
// Map it high in memory, followed by an invalid
// guard page.
char *pa = kalloc();
if(pa == 0)
panic("kalloc");
uint64 va = KSTACK((int) (p - proc));
kvmmap(va, (uint64)pa, PGSIZE, PTE_R | PTE_W);
p->kstack = va;
// char *pa = kalloc();
// if(pa == 0)
// panic("kalloc");
// uint64 va = KSTACK((int) (p - proc));
// kvmmap(va, (uint64)pa, PGSIZE, PTE_R | PTE_W);
// p->kstack = va;
}
kvminithart();
// kvminithart();
}
// Must be called with interrupts disabled,
@@ -121,6 +121,21 @@ found:
return 0;
}
// 初始化一个进程的内核页表
p->prockpt = prockptinit();
if(p->prockpt == 0){
freeproc(p);
release(&p->lock);
return 0;
}
char *pa = kalloc();
if(pa == 0)
panic("kalloc");
uint64 va = KSTACK((int) (p - proc));
uvmmap(p->prockpt, va, (uint64)pa, PGSIZE, PTE_R | PTE_W);
p->kstack = va;
// Set up new context to start executing at forkret,
// which returns to user space.
memset(&p->context, 0, sizeof(p->context));
修改scheduler()来加载进程的内核页表到核心的satp寄存器(参阅kvminithart来获取启发)
改写一个新函数uvminithart,同样是之前的问题,修改以能传入页表参数
diff --git a/kernel/vm.c b/kernel/vm.c
index 8137b05..0984442 100644
--- a/kernel/vm.c
+++ b/kernel/vm.c
@@ -56,6 +89,13 @@ kvminithart()
sfence_vma();
}
void
uvminithart(pagetable_t pagetable)
{
w_satp(MAKE_SATP(pagetable));
sfence_vma();
}
// Return the address of the PTE in page table pagetable
// that corresponds to virtual address va. If alloc!=0,
// create any required page-table pages.
没有进程运行时scheduler()应当使用kernel_pagetable
diff --git a/kernel/proc.c b/kernel/proc.c
index dab1e1d..ad394df 100644
--- a/kernel/proc.c
+++ b/kernel/proc.c
@@ -473,8 +513,13 @@ scheduler(void)
// before jumping back to us.
p->state = RUNNING;
c->proc = p;
uvminithart(p->prockpt);
swtch(&c->context, &p->context);
kvminithart();
// Process is done running for now.
// It should have changed its p->state before coming back.
c->proc = 0;
你需要一种方法来释放页表,而不必释放叶子物理内存页面, 在freeproc中释放一个进程的内核页表
diff --git a/kernel/proc.c b/kernel/proc.c
index dab1e1d..ad394df 100644
--- a/kernel/proc.c
+++ b/kernel/proc.c
@@ -130,6 +145,26 @@ found:
return p;
}
void
freeprockpt(pagetable_t prockpt)
{
// similar to the freewalk method
// there are 2^9 = 512 PTEs in a page table.
for(int i = 0; i < 512; i++){
pte_t pte = prockpt[i];
if((pte & PTE_V) && (pte & (PTE_R|PTE_W|PTE_X)) == 0){
// this PTE points to a lower-level page table.
uint64 child = PTE2PA(pte);
freeprockpt((pagetable_t)child);
prockpt[i] = 0;
} else if(pte & PTE_V){
continue;
}
}
kfree((void*)prockpt);
}
// free a proc structure and the data hanging from it,
// including user pages.
// p->lock must be held.
@@ -143,6 +178,11 @@ freeproc(struct proc *p)
proc_freepagetable(p->pagetable, p->sz);
p->pagetable = 0;
p->sz = 0;
if(p->prockpt){
uvmunmap(p->prockpt, p->kstack, 1, 1);
freeprockpt(p->prockpt);
}
p->prockpt = 0;
p->pid = 0;
p->parent = 0;
p->name[0] = 0;
将需要的函数定义添加到 kernel/defs.h 中
diff --git a/kernel/defs.h b/kernel/defs.h
index ebc4cad..2e302d5 100644
--- a/kernel/defs.h
+++ b/kernel/defs.h
@@ -159,9 +159,12 @@ int uartgetc(void);
// vm.c
void kvminit(void);
pagetable_t prockptinit(void);
void kvminithart(void);
void uvminithart(pagetable_t);
uint64 kvmpa(uint64);
void kvmmap(uint64, uint64, uint64, int);
void uvmmap(pagetable_t, uint64, uint64, uint64, int);
int mappages(pagetable_t, uint64, uint64, uint64, int);
pagetable_t uvmcreate(void);
void uvminit(pagetable_t, uchar *, uint);
修改 kvmpa() 函数
kvmpa() 函数用于将内核虚拟地址转换为物理地址, 其中调用 walk() 函数时使用了全局的内核页表. 此时需要换位当前进程的内核页表
若不进行改动会出现 panic: kvmpa 或者 virtio_disk_intr status 的错误
diff --git a/kernel/vm.c b/kernel/vm.c
index 8137b05..0984442 100644
--- a/kernel/vm.c
+++ b/kernel/vm.c
@@ -5,6 +5,8 @@
#include "riscv.h"
#include "defs.h"
#include "fs.h"
#include "spinlock.h"
#include "proc.h"
/*
* the kernel's page table.
@@ -132,7 +179,8 @@ kvmpa(uint64 va)
pte_t *pte;
uint64 pa;
pte = walk(kernel_pagetable, va, 0);
// pte = walk(kernel_pagetable, va, 0);
pte = walk(myproc()->prockpt, va, 0);
if(pte == 0)
panic("kvmpa");
if((*pte & PTE_V) == 0)