Speed up systems calls

题目要求:为了加速某些系统调用,可以让用户空间和内核空间共享一片只读的物理内存空间,并放在TRAPFRAME下面

根据题目意思就是放在红框这里

image-20231022211710321

首先需要在proc.h文件中的proc结构体添加一个成员:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct proc {
struct spinlock lock;
// p->lock must be held when using these:
enum procstate state; // Process state
void *chan; // If non-zero, sleeping on chan
int killed; // If non-zero, have been killed
int xstate; // Exit status to be returned to parent's wait
int pid; // Process ID

// wait_lock must be held when using this:
struct proc *parent; // Parent process

// these are private to the process, so p->lock need not be held.
uint64 kstack; // Virtual address of kernel stack
uint64 sz; // Size of process memory (bytes)
pagetable_t pagetable; // User page table
struct trapframe *trapframe; // data page for trampoline.S
struct context context; // swtch() here to run process
struct file *ofile[NOFILE]; // Open files
struct inode *cwd; // Current directory
char name[16]; // Process name (debugging)
struct usyscall *usyscallpage; // 共享内存页
};

然后在proc.c的函数allocproc多分配一个额外的物理页面,并且存放进程的pid,建议是放在trapframe代码的下面:

image-20231022212202336

1
2
3
4
5
6
7
// 分配一个user调用的页
if ((p->usyscallpage = (struct usyscall *)kalloc()) == 0) {
freeproc(p);
release(&p->lock);
return 0;
}
p->usyscallpage->pid = p->pid;

并且需要在proc_pagetable函数中完成物理地址和虚拟地址的映射,也建议放在trampoline下面:

image-20231022212425524

1
2
3
4
5
6
if (mappages(pagetable, USYSCALL, PGSIZE, (uint64)(p->usyscallpage), PTE_R | PTE_U) < 0) {
uvmunmap(pagetable, TRAMPOLINE, 1, 0);
uvmunmap(pagetable, TRAPFRAME, 1, 0);
uvmfree(pagetable, 0);
return 0;
}

最后就需要在进程释放函数freeproc中释放掉这个页面:

image-20231022212535555

1
2
3
if (p->usyscallpage)
kfree((void *)p->usyscallpage);
p->usyscallpage = 0;

最最最后一步就是解除映射在proc_freepagetable函数中:

image-20231022212735483

1
uvmunmap(pagetable, USYSCALL, 1, 0);

至此第一个任务算是对大功告成了,其实我觉得这部分还挺麻烦的,而且还没怎么给提示,不知道为啥标记了个简单,我觉得第三个比这个容易写多了。。。

题目要求:设计一个函数打印页表

这部分就毕竟简单了,一个dfs就搞定了。

根据题目要求完成vm.c文件中的vmprint函数,为了方便遍历,我还定义了一个dfs,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void dfs(pagetable_t pagetable, int level) {
for (int i = 0; i < 512; i++) {
pte_t pte = pagetable[i];
if (pte & PTE_V) {
for (int j = level; j <= 2; j++) {
printf(" ..");
}
printf("%d: pte %p pa %p\n", i, pte, PTE2PA(pte));
if (level != 0) {
uint64 child = PTE2PA(pte);
dfs((pagetable_t)child, level - 1);
}
}
}
}
void vmprint(pagetable_t pagetable) {
printf("page table %p\n", pagetable);
dfs(pagetable, 2);
}

然后需要在defs.h中声明一个这个函数

1
void    vmprint(pagetable_t);

至此第二个任务也完成了

Detecting which pages have been accessed

题目要求:给页表添加一位用于标记是否被访问,并实现sys_pgaccess系统调用

image-20231022213259048

通过查阅上图发现,访问位是6,因此先在riscv/h文件中定义一个宏

1
#define PTE_A (1L << 6)  // access bit

然后在sysproc.c文件中实现该系统调用,通过查看pgaccess_test函数发现,需要传入三个参数,首先调用argaddrargint获取到系统调用的三个参数,然后调用walk函数得到每个需要确认的每个0级页表PTE,判断页面标志来确认是否被访问,最后不要忘记将页面的PTE_A标志清0:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int sys_pgaccess(void) {
// lab pgtbl: your code here.
uint64 addr, va;
int len;
uint64 mask = 0;
argaddr(0, &addr);
argint(1, &len);
argaddr(2, &va);
struct proc *p = myproc();
for(int i = 0; i < len; i++) {
pte_t *pte = walk(p->pagetable, addr + i * PGSIZE, 0);
if(*pte & PTE_A) {
mask |= 1 << i;
*pte &= ~PTE_A; // 将访问位清零
}
}
if(copyout(p->pagetable, va, (char *)&mask, sizeof(mask)) < 0)
return -1;
return 0;
}

最后第三个任务也大功告成

测试结果

image-20231022211453629