Lab3 Page Tables

开学!

git

1
2
3
git fetch
git checkout pgtbl
make clean

Speed up system calls

为了优化 getpid 系统调用,不用每次进入内核态获取 PID,创建一个用户可读的页,将 USYSCALL 映射到该页上

可以观察 ugetpid 函数的定义,它直接访问 USYSCALL 即可拿到 pid,不需要系统调用,算是以空间换时间

user/ulib.c
1
2
3
4
5
6
int
ugetpid(void)
{
struct usyscall *u = (struct usyscall *)USYSCALL;
return u->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, TRAPFRAME, 1, 0);
uvmunmap(pagetable, TRAMPOLINE, 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;

...
}
  1. proc_freepagetable 取消页面映射(这里实验文档没说,要自己发现在 freeproc 函数中调用了这个函数)
kernel/proc.c
1
2
3
4
5
6
7
8
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);
}

提问:还有什么其他的系统调用可以通过这样的共享页来加快速度?

怎么感觉没有了

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
void
pteprint(pagetable_t pagetable, int level)
{
for(int i = 0; i < 512; i++) {
pte_t pte = pagetable[i];
if(pte & PTE_V) {
uint64 child = PTE2PA(pte);
for(int j = 0; j < level; j++)
printf(" ..");
printf("%d: pte %p pa %p\n", i, pte, child);
if((pte & (PTE_R | PTE_W | PTE_X)) == 0)
pteprint((pagetable_t)child, level+1);
}
}
}

void
vmprint(pagetable_t pagetable)
{
printf("page table %p\n", pagetable);
pteprint(pagetable, 1);
}

笔者之前使用局部静态变量来判断 level,但是想着如果是多线程的话没有加锁可能会出问题

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

Detect which pages have been accessed

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

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

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

kernel/sysproc.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
26
27
28
29
30
31
32
33
34
35
#define PTE_A (1L << 6)

int
sys_pgaccess(void)
{
uint64 base;
uint64 mask;
int len;
unsigned int abits;

argaddr(0, &base);
argint(1, &len);
if(len > 32)
return -1;
argaddr(2, &mask);

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

for(int i = 0; i < len; i++) {
pte_t *pte = walk(pagetable, base + PGSIZE * i, 0);
if(pte == 0)
return -1;

if(*pte & PTE_A) {
abits |= 1 << i;
*pte &= ~PTE_A;
}
}

if(copyout(pagetable, mask, (char*)&abits, sizeof(abits)) < 0)
return -1;

return 0;``
}

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

Optional challenge exercises

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

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

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

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

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

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

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

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

作者

Humoooor

发布于

2022-10-23

更新于

2024-01-04

许可协议

评论