# 特性：页面管理 & 映射

riscv有很多种管理页面的手段。对于32位的机器，riscv-32一般采用**sv32**的方式管理页面；而对于64位的机器，**sv39**成为riscv64上广受欢迎的方案。

xv6-riscv采用的即为sv39的方式：

![上：sv39虚拟页面映射；下：一个sv39页表项](https://2176209168-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MYgbor9qvQauOjKlp2h%2F-MZ5Rvj9DCtNLk0bijoa%2F-MZ5lDovOnepJUW6uW24%2Friscv_pagetable.png?alt=media\&token=2fbb9281-642d-4670-8e83-1bdb5febc586)

riscv64采用64位的地址，在启动分页前为物理地址，启动后则使用虚拟地址。sv39标准下，虚拟地址只使用低39位，而高25位不予采用；64位的页表项（page table entry，PTE）仅使用低54位，高10位不予采用。这两个未采用部分都可以作为新标准可能需要的扩展空间。

分页模式的架构如下：

* **虚拟地址（virtual address，VA）**&#x662F;启用分页模式之后系统直接使用的地址。它分为**虚拟页号（virtual page number，VPN）**&#x548C;**偏移量（offset）**&#x4E24;部分。VPN又通常分为**若干个字段**，不同字段对应分页寻址中的不同等级的页表寻址；
* **物理地址（physical address，PA）**&#x662F;数据在实际的物理存储元件中的地址。它由寻址过程中最后一步得到；
* **基页面**为页面的最小单位，由页表来找到相应的地址。大小一般为**4KB**；
* **页表项（page table entry，PTE）**&#x4E3A;记载页面地址信息的数据结构，由物理页面&#x53F7;**（physical page number，PPN）**&#x548C;标志位（flags）组成；
* **页表**为页表项的集合，被划分为多个等级，每一个页表拥有若干PTE，由更高级的页表来找到对应的位置。最高级别的页表不需要另外寻找位置，而是在启动分页模式时手动将它的位置存放在satp寄存器的低位PPN处。**页表本身也是一个基页面。**
* **satp（Supervisor Address Translation and Protection）寄存器**存放关于分页系统的设置。高位包含了选择分页模式的MOD字段，优化分页性能的ASID字段；低位包含了**PPN**字段，其长度与PTE内的长度**完全相同。**

具体来说，设**PAGESIZE**为基页面大小（前面提到了，这个值目前为定值**4096**），**PTESIZE**为单个PTE大小，拥有**k**级分页的分页寻址过程如下：

1. **satp**的**PPN**字段提供最高级页表位置，VA的最高级VPN字段——**VPN\[k-1]**&#x63D0;供页内偏移量，寻找第**satp.PPN**个基页面处页表的第**VPN\[k-1]个**PTE，其位置为 $$(satp.PPN\times PAGESIZE)+(VPN\_{k-1}\times PTESIZE)$$ ;
2. 新获得的**PTE(1)**&#x7684;**PPN**提供下一级页表位置，VA的下一级字段——**VPN\[k-2]**&#x63D0;供页内偏移量，寻找第**PTE(1).PPN**个基页面处页表的第**VPN\[k-2]个**PTE，其位置为 $$(PTE\_{1}.PPN\times PAGESIZE)+(VPN\_{k-2}\times PTESIZE)$$ ;
3. 新获得的**PTE(2)**&#x7684;**PPN**提供下一级页表位置，VA的下一级字段——**VPN\[k-3]**&#x63D0;供页内偏移量，寻找第**PTE(2).PPN**个基页面处页表的第**VPN\[k-3]个**PTE，其位置为 $$(PTE\_{2}.PPN\times PAGESIZE)+(VPN\_{k-3}\times PTESIZE)$$ ;
4. ......

最终，寻找到PTE(k)的时候，我们把它的PPN与VA的最后一部分——offset拼接起来，最后就可以得到我们的物理地址**PA**。它的长度为PPN的长度加上offset的长度。

![sv32的寻址示例；sv39的原理基本一致](https://2176209168-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MYgbor9qvQauOjKlp2h%2F-MZ7-WuHlmjgbDLL0Gd3%2F-MZ78e5FZJyUdo8UVHAQ%2Fimage.png?alt=media\&token=b25f6ffb-5fd8-4f83-9114-aa382b90e386)

![sv32的PTE](https://2176209168-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MYgbor9qvQauOjKlp2h%2F-MZC8P8OKOJU67qfI7L5%2F-MZCAKJFBx6L6Jx1nP0t%2Fimage.png?alt=media\&token=2c353de4-c55e-4335-9e4b-18d5f110bc41)

以sv32为例：

* sv32的**PTE**为4个字节大小，flags占低10位，**PPN**占之后的22位；
* 一个4KB的页表能容纳的PTE数目即为 $$2^{10}$$ 个，与之对应的是**每一个字段的VPN**长度为10位，从而实现查找到页表中每一个PTE；
* 正如其名字一样，sv32的虚拟地址为32位长，且这种方案为二级分页，因此VA中**VPN总长**为20位，剩余低12位交给**offset**；
* 最终**PA**为PPN和offset的拼接，共34位，即支持最大16GB的物理内存。

sv39的原理和sv32几乎完全相同，除了一些小差别：

* sv32的**PTE**为8个字节大小，flags占低10位，**PPN**占之后的44位；
* 页表中PTE数目相应调整为 $$2^{9}$$ 个，**每一字段VPN**长度调整为9位；
* sv39虚拟地址可用部分为39位长，三级分页，**VPN总长**27位，低12位交给**offset**；
* 自然，**PA**共56位，支持64PB的物理内存。
