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)记录物理页号与标志位。常见权限/状态位:
| 位 | 含义 |
|---|---|
V | PTE 有效 |
R / W / X | 可读 / 可写 / 可执行 |
U | 用户态可访问 |
G | 全局映射 |
A / D | 已访问 / 已写脏状态 |
若 R/W/X 全为 0,有效 PTE 可作为下一级页表指针;若设置叶子权限,则映射到实际物理页。无效地址、权限不符、非法的 PTE 组合等会产生页故障。内核页通常不设置 U 位,所以普通用户指令不能访问它们。
4. xv6 如何使用页表#
satp 选择地址转换模式并保存根页表的物理页号。内核页表映射内核代码/数据、RAM 和设备所需地址;每个进程有自己的用户页表,映射程序、数据、栈及所需的高地址辅助页。
xv6 用户页表还需支持 trap 入口:trampoline 在切换页表时提供共同代码入口,trapframe 保存用户寄存器并由内核访问。读 kernel/vm.c、kernel/trampoline.S 和 kernel/proc.h,可以对照虚拟地址常量、映射标志和实际使用处。
fork 为子进程建立地址空间并复制用户内存的基础实现;Linux 常用 Copy-on-Write 是另一种减少立即复制的策略,不能直接套用到 xv6 基础版本。
5. TLB 与用户内存拷贝#
TLB 缓存近期地址转换,减少每次访问都遍历页表的开销。更换页表或修改映射后,内核需遵循架构规定使相关旧翻译失效(例如通过 sfence.vma,并考虑多核上的一致性)。
用户程序传入的指针是用户虚拟地址,内核不能把它当作普通内核指针随意解引用。xv6 的 copyin、copyout、copyinstr 会检查并转换/复制用户地址范围中的数据:
copyin:把用户内存的数据读入内核缓冲区。copyout:把内核数据写到用户地址空间。copyinstr:读取用户字符串直到终止符或长度上限。
核心结论: 页表不仅做地址翻译,也执行权限隔离;所有来自用户空间的地址和长度都必须按用户映射规则验证。
下一篇: 11 · xv6 系统调用与 Trap → 12 · xv6 文件系统