MIT6.s081-2020 Lab3 Page Tables
在本实验中,您将探索页表并修改它们以简化将数据从用户空间复制到内核空间的函数。
准备
git fetch
git checkout pgtbl
make clean
Print a page table
引言
为了帮助您了解 RISC-V 页表,并且可能有助于将来的调试,首要任务是编写一个打印页表内容的函数。
定义一个名为vmprint()
. 它应该需要一个pagetable_t
参数,并以下面描述的格式打印该页表。插入if(p->pid==1) vmprint(p->pagetable)
在 exec.c
之前return argc
, 打印第一个进程的页表。
实现
将vmprint
函数添加到kernel/vm.c
文件中并在kernel/defs.h
文件中添加该函数的声明。
//vm.c
//...
int copyinstr(pagetable_t, char *, uint64, uint64);
void vmprint(pagetable_t);
使用格式符%p
打印16进制数,使用kernel/riscv.h
文件中定义的宏, 参考freewalk
函数以递归的形式完成。
//kernel/riscv.h
#define PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1))
#define PGROUNDDOWN(a) (((a)) & ~(PGSIZE-1))
#define PTE_V (1L << 0) // valid
#define PTE_R (1L << 1)
#define PTE_W (1L << 2)
#define PTE_X (1L << 3)
#define PTE_U (1L << 4) // 1 -> user can access
#define PTE2PA(pte) (((pte) >> 10) << 12)
//...
typedef uint64 pte_t;
typedef uint64 *pagetable_t; // 512 PTEs
//kernel/vm.c
// Recursively free page-table pages.
// All leaf mappings must already have been removed.
void
freewalk(pagetable_t pagetable)
{
// there are 2^9 = 512 PTEs in a page table.
for(int i = 0; i < 512; i++){
pte_t pte = pagetable[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);
freewalk((pagetable_t)child);
pagetable[i] = 0;
} else if(pte & PTE_V){
panic("freewalk: leaf");
}
}
kfree((void*)pagetable);
}
需要注意的说遍历到最后一层页表就停止,最后一层页表中页表项中W位,R位,X位起码有一位会被设置为1。
void vmprint_helper(pagetable_t pagetable,int dep) {
for(int i = 0; i < 512; i++){
pte_t pte = pagetable[i];
if(pte & PTE_V){
for(int j = 0; j < dep; j++) {
if(j != 0){
printf(" ");
}
printf("..");
}
// this PTE points to a lower-level page table.
uint64 child = PTE2PA(pte);
printf("%d: pte %p pa %p\n",i,pte,child);
if((pte & (PTE_R|PTE_W|PTE_X)) == 0){
vmprint_helper((pagetable_t)child,dep + 1);
}
}
}
}
//Print a page table
void vmprint(pagetable_t pagetable){
// there are 2^9 = 512 PTEs in a page table.
printf("page table %p\n",pagetable);
vmprint_helper(pagetable,1);
}
结果
实验测试结果:
A kernel page table per process
引言
Xv6 有一个内核页表,每当它在内核中执行时都会使用它。内核页表直接映射到物理地址,因此内核虚拟地址x映射到物理地址x。Xv6 还为每个进程的用户地址空间提供了一个单独的页表,仅包含该进程的用户内存的映射,从虚拟地址零开始。因为内核页表不包含这些映射,所以用户地址在内核中是无效的。因此,当内核需要使用在系统调用中传递的用户指针(例如,传递给write()
),内核必须首先将指针转换为物理地址。本节和下一节的目标是允许内核直接取消引用用户指针.
实现
这一部分是要求我们给每个进程维护一个各自不同的内核页表,当陷入内核时,切换到这个特有内核页表,内核就直接可以使用虚拟地址来访问系统调用参数了。按照HINT一步步做下去即可。
HINT1