xv6 中地址空间的创建、TLB 处理与 sfence.vma 指令详解
1. 地址空间创建(Creating Address Space)
xv6 的地址空间通过多级页表(RISC-V Sv39,三级)实现虚拟地址到物理地址的映射。根据用途可分为:
- 内核地址空间
- 用户进程地址空间
a. 内核地址空间
系统启动时创建,仅一个实例,所有内核态共享。
kvmmake()
-
创建内核页表的入口:
kpgtbl = (pagetable_t) kalloc(); memset(kpgtbl, 0, PGSIZE); -
使用
kvmmap()建立各类内核映射。
kvmmap()
内部调用 mappages() 处理页表项,具体映射包括:
-
MMIO 映射(恒等映射)
kvmmap(kpgtbl, UART0, UART0, PGSIZE, PTE_R | PTE_W); kvmmap(kpgtbl, VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W); kvmmap(kpgtbl, PLIC, PLIC, 0x4000000, PTE_R | PTE_W); -
内核代码段映射
kvmmap(kpgtbl, KERNBASE, KERNBASE, etext - KERNBASE, PTE_R | PTE_X); -
内核数据段映射
kvmmap(kpgtbl, etext, etext, PHYSTOP - etext, PTE_R | PTE_W); -
trampoline 页(高地址处)
kvmmap(kpgtbl, TRAMPOLINE, trampoline, PGSIZE, PTE_R | PTE_X);
b. 用户地址空间
每个进程独立拥有,生命周期随进程创建/销毁。
uvmcreate()
创建根页表页并清零,返回空页表。
地址空间填充相关函数:
uvmfirst():为第一个用户进程init分配页面并加载代码。uvmalloc():进程扩展内存时分配新页并建立映射。uvmcopy():fork 时复制父进程内存到子进程。
c. 页表操作机制
walk()
模拟三级页表查找过程:
- 遇到缺失页表时可选分配。
- 最终返回最底层 PTE 指针。
mappages()
结合 walk 为虚拟地址范围建立映射,设置权限。
2. TLB 的处理
a. 什么是 TLB?
- TLB(Translation Lookaside Buffer)是 CPU 中的高速缓存。
- 缓存最近使用的 VA→PA 映射(PTE)。
b. 为什么需要处理 TLB?
修改页表(如 mappages 或 uvmunmap)后:
- TLB 中可能还存在过期映射
- 若不刷新,CPU 可能使用旧权限或访问已释放页,造成错误。
解决方式:刷新 TLB,通知 CPU 不再使用旧条目。
3. sfence.vma 指令
RISC-V 的Supervisor Fence for Virtual Memory Access 指令,用于刷新 TLB。
a. sfence.vma 的作用
- 保证之后的地址访问会使用最新页表
- 防止使用已失效的页表缓存项
b. 在 xv6 中的使用
位于 kvminithart() 中,每个 CPU 启动时执行:
void kvminithart() {
sfence_vma(); // 第一次:确保页表写入完成
w_satp(MAKE_SATP(kernel_pagetable)); // 加载页表
sfence_vma(); // 第二次:确保使用新页表,清除旧 TLB 项
}
执行顺序说明:
-
第一次
sfence_vma()确保页表初始化已生效 -
设置 satp 启用分页并指定页表根地址
-
第二次
sfence_vma()清除 TLB 中的旧条目,强制使用新页表
总结
- 地址空间创建: 内核和用户通过页表映射管理内存
- TLB 问题: 硬件缓存可能滞后,需及时清除
- sfence.vma: 指令用于强制 TLB 刷新,确保页表更新生效