RISC-V指令(用户指令、内核指令)使用的都是虚拟地址。
RISC-V页表硬件通过将每一个虚拟地址映射到物理地址来为这两种地址建立联系。
只使用64位虚拟地址的低39位,高25位不用。
RISC-V页表在逻辑上是由2^27个页表条目(Page Table Enrties,PTE)组成的数组。每个PTE包含一个44位的物理页码(Physical Page Number, PPN)和一些标志。页式硬件通过使用虚拟地址39位中的前27位索引页表,以找到该虚拟地址对应的PTE,然后生成一个56位的物理地址,其前44位来自PTE中的PPN,其后12位来自原始虚拟地址。image.png
2^27个页表条目以一个三级树的形式进行存储,三个2^9确定最终的PTE
一个虚拟地址
image.png
PTEV指示PTE是否存在:如果它没有被设置,对页面的引用会导致异常(即不允许)。
PTE_R控制是否允许指令读取到页面。
PTE_W控制是否允许指令写入到页面。
PTE_X控制CPU是否可以将页面内容解释为指令并执行它们。
PTE_U控制用户模式下的指令是否被允许访问页面;如果没有设置PTE_U,PTE只能在管理模式下使用。
图3.2显示了它是如何工作的。标志和所有其他与页面硬件相关的结构在(**_kernel/riscv.h
**)中定义。

Print a page table

定义一个函数vmprint(pagetable_t)并打印pagetable,在exec.c中的返回参数return argc之前添加if(p->pid==1) vmprint(p->pagetable) 以此来打印第一个进程的pagetable
实现这个功能后,启动xv6应该会打印这样的输出,描述第一个进程在完成exec()执行init后的pagetable

  1. 第一行显示vmprint()的参数,之后每一行对应一个PTE
  2. 每行使用“..”表示树的深度
  3. 每个PTE行显示PTE在pagetable page中的索引、pte地址、pte中的物理页地址

    kernel/defs.h 中保存了函数的声明

    A kernel page table per process

  4. 无论何时在内核执行时,xv6使用同一个内核页表。内核页表是一个物理地址的直接映射,因此内核虚拟地址x对应物理地址x。

  5. xv6也有一个单独的页表给每个进程的用户地址空间,仅包含那个进程用户内存的映射,起始于虚拟地址0。
  6. 因为内核页表不包含这些映射,用户地址在内核无效。因此,当内核需要使用一个用户指针传到system call时,内核必须首先翻译指针到物理地址。
  7. 这个和下个实验的目的是为了允许内核直接解析用户指针。
  8. 第一个任务是更改内核,为了当在内核执行时,每个进程使用它自己的内核页表拷贝。
  9. 更改struct proc来让每个进程保持一个内核页表,更改scheduler(),当切换进程时切换内核页表。
  10. 对于这一步,每个进程的内核页表应该和已存在的全局内核页表完全相同。
  11. 读xv6书、理解作业一开始提到的代码如何起作用,将更容易正确地更改虚拟内存代码。
  12. 页表设置中的bug会因为缺少映射导致缺陷,导致加载、存储会影响到不可预期的物理内存页,也会导致执行不正确的物理内存页。