Lab3 Page Tables

开学!

git

1
2
3
git fetch
git checkout pgtbl
make clean

Speed up system calls

为了优化 getpid 系统调用,不用每次进入内核态获取 PID,创建一个用户可读的页,将 USYSCALL 映射到该页上,在调用 ugetpid 时,直接访问 USYSCALL 即可拿到 pid,不需要系统调用,算是以空间换时间

这里不同的是,页表映射实际上是虚拟地址映射到虚拟地址,而不是映射到物理地址

  1. kernel/proc.h 中 proc 结构体加入 struct usyscall *usyscall
  2. allocproc 初始化 usyscall
kernel/proc.c
1
2
3
4
5
6
7
8
9
10
11
12
13
static struct proc* allocproc(void)
{
...

if((p->usyscall = (struct usyscall *)kalloc()) == 0){
freeproc(p);
release(&p->lock);
return 0;
}
p->usyscall->pid = p->pid;

...
}
  1. proc_pagetable 建立映射
kernel/proc.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
pagetable_t proc_pagetable(struct proc *p)
{
...

if(mappages(pagetable, USYSCALL, PGSIZE,
(uint64)(p->usyscall), PTE_R | PTE_U) < 0){
uvmunmap(pagetable, TRAMPOLINE, 1, 0);
uvmunmap(pagetable, TRAPFRAME, 1, 0);
uvmfree(pagetable, 0);
return 0;
}

...
}
  1. 在 freeproc 释放 usyscall
kernel/proc.c
1
2
3
4
5
6
7
8
9
10
static void freeproc(struct proc *p)
{
...

if(p->usyscall)
kfree((void*)p->usyscall);
p->usyscall = 0;

...
}

xv6 使用三级页表,在运行第一个用户进程时打印出其页表

这里使用一个静态变量 level 表示在第几级页表

kernel/vm.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void vmprint(pagetable_t pagetable) {
static int level = 0;
if(level == 0) {
level++;
printf("page table %p\n", pagetable);
}

for(int i = 0; i < 512 && level <= 3; ++i) {
pte_t pte = pagetable[i];

if(pte & PTE_V) {
for(int j = 0; j < level; ++j) {
printf(" ..");
}
printf("%d: pte %p pa %p\n", i, pte, PTE2PA(pte));
level++;
vmprint((pagetable_t)PTE2PA(pte));
level--;
}
}

if(level == 1) {
level--;
}
}

看到 wingsgg 用 pte & (PTE_R|PTE_W|PTE_X) == 0 表示是否中间页表,确实很棒,但是用了两个函数

笔者已经用一个函数写好了,觉得也还行

然后在 exec.c 和 defs.h 中添加声明和使用就行

Detect which pages have been accessed

RISC-V 硬件会在 TLB 命中失败时,将对应 PTE 的 Access 标志位设 1,用来记录该页面有没有访问过

写一个系统调用,三个参数,检测的虚拟地址,检测的页数,bitmask

挺简单的,不知道为什么实验难度写着 hard

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#define PTE_A (1L << 6)

int
sys_pgaccess(void)
{
uint64 buf;
int n;
uint64 uabits;

argaddr(0, &buf);
argint(1, &n);
argaddr(2, &uabits);

uint abits = 0;
pagetable_t pagetable = myproc()->pagetable;

for(int i = 0; i < n; ++i) {
pte_t *pte;
pte = walk(pagetable, buf + i * PGSIZE, 0);
if(!pte) {
return -1;
}
if(*pte & PTE_A) {
abits |= 1 << i;
*pte &= ~PTE_A;
}
}
copyout(pagetable, uabits, (char*)&abits, sizeof(abits));
return 0;
}

注意检测完后,将标记置零,不然不知道检测后还没有访问过

Optional challenge exercises

使用 super-pages 减少页表中 PTE 的数量

不是很懂,改用更大的页(?)

取消用户进程的第一页的映射,

这样可以使引用空指针直接造成错误

需要修改 user.ld 文件,让进程的 text 段从 0x1000 开始,而不是 0

估计要改很多东西(uvmmap,uvmalloc啥的)。。。咕咕咕

添加一个系统调用报告 dirty pages(修改过的页表)

和第三个差不多,就不做了

作者

Humoooor

发布于

2022-10-23

更新于

2022-11-17

许可协议

评论