MIT 6.1810 Lab: page tables
Speed up system calls (easy)
任务
通过在内核空间和用户空间之间通过共享只读存储区域内的数据,加速特定的系统调用。在每个进程被创建时,将一个只读的页映射到 USYSCALL。在这一页的开始部分,存储一个 struct usyscall 结构体,将它初始化来存储当前的进程的 pid。
实现
- 在
kernel/proc.h
中的 struct proc 定义中添加:
struct usyscall *usyscall; // 加速系统调用,指向共享的页的指针
- 在
kernel/proc.c
中的 proc_pagetable() 添加:
if (mappages(pagetable, USYSCALL, PGSIZE, (uint64)(p->usyscall), PTE_R))
{
uvmunmap(pagetable, TRAMPOLINE, 1, 0);
uvmunmap(pagetable, TRAPFRAME, 1, 0);
uvmfree(pagetable, 0);
return 0;
}
- 在
kernel/proc.c
中的 allocproc() 添加:
// 为 usyscall 分配一页
if ((p->usyscall = (struct usyscall *)kalloc()) == 0)
{
freeproc(p);
release(&p->lock);
return 0;
}
p->usyscall->pid = p->pid;
- 在
kernel/proc.c
中的 free_pagetable() 添加:
uvmunmap(pagetable, USYSCALL, 1, 0);
- 在
kernel/proc.c
中的 freeproc() 添加:
if (p->usyscall)
kfree((void *)p->usyscall);
p->usyscall = 0;
Print a page table (easy)
任务
编写一个函数,接收 pagetable_t 参数,按照特定格式打印出页表。在 kernel/exec.c
中 return argc 之前添加 if(p->pid==1) vmprint(p->pagetable),来打印出第一个进程的页表。
实现
- 在
kernel/vm.c
中添加:
void print_pagetable(pagetable_t pagetable, int depth)
{
for (int i = 0; i < 512; i++)
{
pte_t pte = pagetable[i];
if (pte & PTE_V)
{
uint64 addr = PTE2PA(pte);
printf("..");
for (int i = 1; i < depth; ++i)
printf(" ..");
printf("%d: pte %p pa %p\n", i, pte, addr);
if (depth < 3)
print_pagetable(addr, depth + 1);
}
}
}
void vmprint(pagetable_t pagetable)
{
printf("page table %p\n", pagetable);
print_pagetable(pagetable, 1);
}
- 在
kernel/defs.h
中添加定义:
void vmprint(pagetable_t);
- 在
kernel/exec.c
中 return argc; 前添加:
vmprint(pagetable);
Detect which pages have been accessed (hard)
任务
实现一个系统调用 pgaccess(),报告哪些 page 被访问过。系统调用接收三个参数:第一个要检查的 user page 的虚拟地址,要检查的 page 的数量,一个 user 地址,指向缓冲区用来将结果存入一个 bitmask。
实现
-
对应的系统调用已经添加,还需要添加系统调用的具体实现。
-
首先在
kernel/riscv.h
中添加 PTE_A 的定义。
#define PTE_A (1L << 6) // the access bit
- 在
kernel/sysproc
中的 sys_pgaccess() 中添加从用户空间获取参数并实现系统调用。
int
sys_pgaccess(void)
{
uint64 buf;
int num;
uint64 maskaddr;
struct proc *p = myproc();
uint64 mask;
argaddr(0, &buf);
argint(1, &num);
argaddr(2, &maskaddr);
if (num > 64)
return -1;
mask = 0;
for (uint64 i = 0; i < num; ++i)
{
pte_t *pte;
pte = walk(p->pagetable, buf + i * PGSIZE, 0);
// 如果 page 已经被分配且被访问过
if (pte != 0 && ((*pte) & PTE_A))
{
mask = mask | (1L << i);
// 将 PTE_A 位重新设置为 0
*pte = (*pte) & ~PTE_A;
}
}
if (copyout(p->pagetable, maskaddr, (char *)&mask, sizeof(mask)) < 0)
return -1;
return 0;
}