MIT 6.S081 Lab pgtbl

Compulsory exercises

Preparation

  • reading
    preparation

  • To start the lab, switch to the pgtbl branch:

1
2
3
git fetch
git checkout pgtbl
make clean

Speed up system calls (easy)

  • getpid()作为系统调用,每次调用时需要跳入内核并跳出,为加速pid的获取,ugetpid()函数可以通过分配虚拟地址将pid存入其中,从而实现不跳转不trap获取pid

  • 修改内核函数实现ugetpid()
    speed

  • map one read-only page at USYSCALL in proc_pagetable()

proc.c proc_pagetable()
1
2
3
4
5
if(mappages(pagetable, USYSCALL, PGSIZE,
(uint64)(p->usyscall), PTE_R | PTE_U) < 0){
uvmfree(pagetable, 0);
return 0;
}
  • allocate and initialize the page in allocproc()

proc.c allocproc()
1
2
3
4
5
6
// Allocate a usyscall page before creating a new pagetable
if((p->usyscall = (struct usyscall *)kalloc()) == 0){
freeproc(p);
release(&p->lock);
return 0;
}
  • free the page in freeproc()

proc.c freeproc()
1
2
3
4
if(p->usyscall) {
kfree((void*)p->usyscall);
p->usyscall = 0;
}
  • umap USYSCALL in proc_freepagetable()

proc.c proc_freepagetable()
1
uvmunmap(pagetable, USYSCALL, 1, 0);
  • RISC-V架构中的三级页表需要通过递归打印

  • 添加内核函数vmprint()
    vmprint

  • prototype for vmprint in defs.h

defs.h //vm.c
1
void            vmprint(pagetable_t);
  • put vmprint() in vm.c

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
// help function
void vmprinthelp(pagetable_t pagetable, int level) {
char *dot;
if (level == 2) dot = "..";
if (level == 1) dot = ".. ..";
if (level == 0) dot = ".. .. ..";
for (int i = 0; i < 512; i++) {
pte_t pte = pagetable[i];
if (pte & PTE_V) {
uint64 child = PTE2PA(pte);
printf("%s%d: pte %p pa %p\n", dot, i, pte, child);
if (level != 0) {
vmprinthelp((pagetable_t)child, level - 1);
}
}
}
}

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

Detecting which pages have been accessed (hard)

  • 探测页表中的页是否被访问过

  • 添加pgaccess系统调用
    pgaccess

  • implementing sys_pgaccess() sysproc.c

sysproc.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifdef LAB_PGTBL
int
sys_pgaccess(void)
{
// lab pgtbl: your code here.
uint64 firstpte;
int num;
uint64 buf;
if (argaddr(0, &firstpte) < 0)
return -1;
if (argint(1, &num) < 0)
return -1;
if (argaddr(2, &buf) < 0)
return -1;

pagetable_t pagetable = myproc()->pagetable;
pgaccess(pagetable, firstpte, num, buf);

return 0;
}
#endif
  • define PTE_A, the access bit, in riscv.h 参考下图PTE_A的位置

riscv.h
1
#define PTE_A (1L << 6) // 1 -> Accessed
  • add pgaccess() in vm.c

    • 注意第一个page对应mask最低有效位
    • 注意探测一个带有PTE_A的page过后要消除PTE_A
vm.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
uint64
pgaccess(pagetable_t pagetable, uint64 firstpte, int num, uint64 buf) {
uint64 mask = 0;
uint64 limit = 64;

for (uint64 i = 0; i < num && i < limit; i++) {
pte_t *pte_ptr;
pte_ptr = walk(pagetable, firstpte + i * (uint64)PGSIZE, 0);
if ((pte_ptr != 0) && (*pte_ptr & PTE_A)) {
mask = mask | (1L << i);
*pte_ptr = *pte_ptr & (~PTE_A);
}
}

copyout(pagetable, buf, (char *)&mask, 8);
return 0;
}

Optional challenge exercises

  • Use super-pages to reduce the number of PTEs in page tables.

  • Unmap the first page of a user process so that dereferencing a null pointer will result in a fault. You will have to start the user text segment at, for example, 4096, instead of 0.

  • Add a system call that reports dirty pages (modified pages) using PTE_D.

作者

huayi

发布于

2023-04-05

更新于

2023-04-23

许可协议