【MIT6.S081】Lab3 page tables(下)

本关的任务为“Detect which pages have been accessed”,需要实现一个新的系统调用pgaccess,它指出访问了哪些页面被访问了(读、写等)。系统调用需要三个参数:

  1. 第一个用户页面的起始虚拟地址
  2. 需要检查页数
  3. 一个存储每一页是否被访问的掩码

该lab的所有代码:Github

测试该系统调用的函数位于user/pgtbltest.c:pgaccess_test()中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void
pgaccess_test()
{
char *buf;
unsigned int abits;
printf("pgaccess_test starting\n");
testname = "pgaccess_test";
buf = malloc(32 * PGSIZE);
if (pgaccess(buf, 32, &abits) < 0)
err("pgaccess failed");
buf[PGSIZE * 1] += 1;
buf[PGSIZE * 2] += 1;
buf[PGSIZE * 30] += 1;
if (pgaccess(buf, 32, &abits) < 0)
err("pgaccess failed");
if (abits != ((1 << 1) | (1 << 2) | (1 << 30)))
err("incorrect access bits set");
free(buf);
printf("pgaccess_test: OK\n");
}

分析下源码可以知道,测试程序分配了32个页,并且使用(or 访问)了这分配的32个页的第1、2、30页,之后程序调用pgaccess来检测abits的第1、2、30位是否为1,即判断该系统调用是否实现了“检测已经访问的页”这个功能。

向内核添加系统调用的方法在lab2中已经了解过了,不过这里xv6已经帮我们添加好了,我们只需要实现系统调用kernel/sysproc.c:sys_pgaccess()即可。

在xv6 book中可以知道一个PTE的每位构成如上图,其中0 - 9位是一些标志位,第6位为Accessed,也就是访问位,需要在内核中添加这个标志:

1
2
3
// kernel/riscv.h

#define PTE_A (1L << 6)

而risc-v处理器会利用硬件将已访问的页的PTE_A正确设置。

实现sys_pgaccess的步骤大致可分为:

  1. 接受用户态传递的三个参数:
    1. 第一个用户页面的起始虚拟地址(指针)
    2. 需要检查页数(int)
    3. 一个存储每一页是否被访问的掩码(指针)
  2. 遍历“需要检查页数”,并每次检查遍历的页是否已访问。获取每个页对应的PTE将使用walk函数来获取,而检查将使用PTE_A来判断;如果当前页的PTE_A为1,则说明该页被访问过,利用位运算(是的,位运算在该lab里立大功)来存储信息,即第几页被访问了,并且不要忘记了实验指导中的提示,“Be sure to clear PTE_A after checking if it is set. Otherwise, it won’t be possible to determine if the page was accessed since the last time pgaccess() was called”,检测完后应该将PTE_A标记位清除。
  3. 将存储的信息利用copyout函数从内核态传递给用户态,届时用户态可通过第三个参数获取。

具体实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int
sys_pgaccess(void)
{
// lab pgtbl: your code here.
uint64 uvm_pgaddr;
int page_counts;
uint64 abits;
argaddr(0, &uvm_pgaddr);
argint(1, &page_counts);
argaddr(2, &abits);

int result = 0;
pagetable_t page_table = myproc()->pagetable;
for (int i = 0; i < page_counts; i++) {
pte_t *pte = walk(page_table, uvm_pgaddr, 0);
if (((*pte) & (PTE_A)) != 0) {
result |= (1 << i);
*pte &= (~PTE_A);
}
uvm_pgaddr += PGSIZE;
}
copyout(page_table, abits, (char*)&result, sizeof(result));
return 0;
}