跳至正文
这篇笔记的目录
回到学习目录
技术开发

xv6 虚拟内存与页表

页表把进程看到的虚拟地址映射到物理页,并用权限位控制读、写、执行和用户态访问。

#RISC-V#xv6#计算机系统#学习笔记

一句话: 页表把进程看到的虚拟地址映射到物理页,并用权限位控制读、写、执行和用户态访问。

本章导航#

虚拟地址 · Sv39 · PTE 权限 · xv6 地址空间 · TLB 与拷贝

学习目标#

  • 手算 Sv39 页内偏移和三级页表索引。
  • 读懂 PTE 的有效位、权限位及 satp 的角色。
  • 理解用户地址空间隔离、trap trampoline 和 copyin/copyout。

1. 为什么要有虚拟内存#

程序使用虚拟地址;硬件的地址转换机制(MMU)根据当前页表找到物理地址。这样,两个进程可以使用相同的虚拟地址而映射到不同物理页,内核也可控制用户代码能访问哪些页。

页面大小通常为 4 KiB(4096 字节)。地址可分为虚拟页号 VPN页内偏移;页内偏移在映射前后保持不变。

2. Sv39 地址与页表遍历#

xv6 RISC-V 使用 Sv39 虚拟内存模式。普通 4 KiB 页的虚拟地址拆成:

| VPN[2] 9 位 | VPN[1] 9 位 | VPN[0] 9 位 | 页内偏移 12 位 |

9+9+9+12=39。Sv39 的有效地址需符合规范的符号扩展/规范地址规则;实际用户地址通常只使用低端允许范围。每张页表占一页,包含 512 个 8 字节 PTE。

satp 中的根页表物理页号
↓ 用 VPN[2] 索引
一级页表项 → 下一级页表
↓ 用 VPN[1] 索引
二级页表项 → 下一级页表
↓ 用 VPN[0] 索引
叶子 PTE → 物理页号 + 页内偏移

规范允许不同层级的叶子映射较大的页;xv6 常见映射主要按 4 KiB 页建立。实际地址转换由架构规则定义,页表遍历可以由硬件完成。

3. PTE 和权限#

PTE(Page Table Entry)记录物理页号与标志位。常见权限/状态位:

含义
VPTE 有效
R / W / X可读 / 可写 / 可执行
U用户态可访问
G全局映射
A / D已访问 / 已写脏状态

R/W/X 全为 0,有效 PTE 可作为下一级页表指针;若设置叶子权限,则映射到实际物理页。无效地址、权限不符、非法的 PTE 组合等会产生页故障。内核页通常不设置 U 位,所以普通用户指令不能访问它们。

4. xv6 如何使用页表#

satp 选择地址转换模式并保存根页表的物理页号。内核页表映射内核代码/数据、RAM 和设备所需地址;每个进程有自己的用户页表,映射程序、数据、栈及所需的高地址辅助页。

xv6 用户页表还需支持 trap 入口:trampoline 在切换页表时提供共同代码入口,trapframe 保存用户寄存器并由内核访问。读 kernel/vm.ckernel/trampoline.Skernel/proc.h,可以对照虚拟地址常量、映射标志和实际使用处。

fork 为子进程建立地址空间并复制用户内存的基础实现;Linux 常用 Copy-on-Write 是另一种减少立即复制的策略,不能直接套用到 xv6 基础版本。

5. TLB 与用户内存拷贝#

TLB 缓存近期地址转换,减少每次访问都遍历页表的开销。更换页表或修改映射后,内核需遵循架构规定使相关旧翻译失效(例如通过 sfence.vma,并考虑多核上的一致性)。

用户程序传入的指针是用户虚拟地址,内核不能把它当作普通内核指针随意解引用。xv6 的 copyincopyoutcopyinstr 会检查并转换/复制用户地址范围中的数据:

  • copyin:把用户内存的数据读入内核缓冲区。
  • copyout:把内核数据写到用户地址空间。
  • copyinstr:读取用户字符串直到终止符或长度上限。

核心结论: 页表不仅做地址翻译,也执行权限隔离;所有来自用户空间的地址和长度都必须按用户映射规则验证。

下一篇: 11 · xv6 系统调用与 Trap12 · xv6 文件系统