【XV6】 page tables

代码:https://github.com/JasenChao/xv6-labs.git

快速获取pid-ugetpid

题目要求参考已实现的ugetpid()使用USYSCALL快速获取pid

实现的思路是在每一个进程中增加一个共享页面,通过USYSCALL指定的虚拟地址,找到指定的页面。参考进程中的Trampoline页Trapframe页。Trampoline页保存进出内核的代码,Trapframe页保存寄存器的数据。

proc.h文件中的struct proc中增加成员struct usyscall *sc

proc.c文件中的allocproc()函数中,模仿struct trapframe,加入以下代码,申请内存并把pid保存下来:

if((p->sc = (struct usyscall *)kalloc()) == 0){
    freeproc(p);
    release(&p->lock);
    return 0;
}
p->sc->pid = p->pid;

对应地修改freeproc函数,释放内存空间:

if(p->sc)
    kfree((void*)p->sc);

proc.c文件中的proc_pagetable函数中,增加USYSCALLPTE

if(mappages(pagetable, USYSCALL, PGSIZE,
            (uint64)(p->sc), PTE_R | PTE_U) < 0){
    uvmunmap(pagetable, TRAPFRAME, 1, 0);
    uvmunmap(pagetable, TRAMPOLINE, 1, 0);
    uvmfree(pagetable, 0);
    return 0;
}

每个 PTE 都包含一些标志位,说明分页硬件对应的虚拟地址的使用权限:

  • PTE_V:表示页表项的有效性(valid),即该页表项是否有效。
  • PTE_R:表示可读权限(read),指示对应的页面是否可以被读取。
  • PTE_W:表示可写权限(write),指示对应的页面是否可以被写入。
  • PTE_X:表示可执行权限(execute),指示对应的页面是否可以被执行。
  • PTE_U:表示用户访问权限(user),指示该页表项是否可以被用户级别的程序访问。

对应地修改proc_freepagetable函数,释放内存:

uvmunmap(pagetable, USYSCALL, 1, 0);

打印页表

题目要求可视化RISC-V页表,在启动XV6的时候打印第一个进程的页表。

根据提示在vm.c中实现打印页表功能的函数,可以参考同文件中的freewalk函数,只遍历页表,不要将页表清空即可。代码如下:

static void vmprint_func(pagetable_t pagetable, int level)
{
  for(int i = 0; i < 512; i++){
    pte_t pte = pagetable[i];
    if(pte & PTE_V){
      uint64 pa = PTE2PA(pte);
      for(int j = 0; j < level; ++j) printf(".. ");
      printf("..%d: pte %p pa %p\n", i, pte, pa);
      if (level < 2) vmprint_func((pagetable_t)pa, level + 1);
    }
  }
}

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

为了让exec函数可以调用以上函数,在defs.h中进行声明:

void            vmprint(pagetable_t pagetable);

exec.c文件中的exec函数返回前增加代码,判断如果pid为1则调用打印页表的功能:

if(p->pid == 1){
    vmprint(p->pagetable);
}

使用make GRADEFLAGS=printout grade命令测试是否通过。

检测已访问的页面

题目要求实现一个系统调用,通过bit位检测页面是否被访问,测试程序pgaccess_test已经给出,用unsigned int abits的32位检测32个页面被访问的情况,置1即为被访问,下面对第1、2和30个页面的内容进行了写操作,正常应该返回abits40000006

先在riscv.h文件中增加#define PTE_A (1L << 6),一开始我按顺序设的5,程序测试不通过,后来发现手册里面必须写6...

再完善sysproc.c文件中的sys_pgaccess函数,函数框架已经给出来了,代码如下:

int
sys_pgaccess(void)
{
  struct proc *p = myproc();
  uint64 addr, mask, va;
  int n;
  unsigned int abits = 0;

  argaddr(0, &addr);
  argint(1, &n);

  va = addr;
  for(int i = 0; i < n; ++i){
    pte_t* pte = walk(p->pagetable, va, 0); // 获取PTE
    if(*pte & PTE_A){
      abits |= 1 << i;
      *pte ^= PTE_A;                        // 如果PTE_A已经置1,需要置为0,避免第二次访问时混淆
    }
    va += PGSIZE;
  }

  argaddr(2, &mask);
  if(copyout(p->pagetable, mask, (char*)&abits, sizeof(abits)) < 0) return -1;
  return 0;
}

使用make GRADEFLAGS=pgaccess grade命令测试结果是否正确。

测试结果

使用make grade测试,结果如下:

== Test   pgtbltest: ugetpid ==
  pgtbltest: ugetpid: OK
== Test   pgtbltest: pgaccess ==
  pgtbltest: pgaccess: OK
== Test pte printout ==
$ make qemu-gdb
pte printout: OK (0.9s)
posted on 2024-02-14 19:04  未连接到互联网  阅读(21)  评论(0编辑  收藏  举报