Xv6中deallocuvm工作原理及PGADDR相关代码逻辑疑问
拆解Xv6中deallocuvm函数的那行“跳步”代码
咱们先从deallocuvm的核心工作说起:它负责把进程的用户地址空间从oldsz缩小到newsz,本质就是遍历需要释放的虚拟地址范围,找到对应的页表项(PTE),如果存在映射就释放物理页、清除PTE,直到处理完所有待释放的地址。
你疑惑的这行代码:
if(!pte) a = PGADDR(PDX(a) + 1, 0, 0) - PGSIZE;
其实是个跳过空页表的效率优化,得结合Xv6的页表结构和循环逻辑来看:
先搞懂几个关键宏的含义
PDX(a):从虚拟地址a里提取页目录项的索引,简单说就是当前地址属于哪一张页表PGADDR(PDX(a)+1, 0, 0):计算下一张页表对应的起始虚拟地址(Xv6里每张页表对应1024个页,覆盖4MB的地址空间)PGSIZE:页大小,Xv6里固定是4KB(0x1000)
为什么要这么写?
deallocuvm的循环默认是按a += PGSIZE逐个页遍历的,但如果某个地址a对应的pte不存在,说明当前整个页表都没有被创建过——因为Xv6的规则是:如果页目录项都不存在,那这个页表下的所有虚拟页肯定都没有映射。这时候再逐个页循环就是纯浪费时间,毕竟这4MB地址空间里连一个有效映射都没有。
这行代码的作用就是直接跳过整个空页表的地址范围:
- 当检测到
!pte(当前页表不存在),先算出下一张页表的起始地址,然后减去一个PGSIZE - 因为循环末尾会执行
a += PGSIZE,所以处理后a会直接跳到下一张页表的起始位置,相当于一次性跳过了当前空页表覆盖的1024个页(4MB空间)
举个实际的例子:
假设当前a是0x100000(属于第一张用户页表的地址范围),如果这张页表不存在,PGADDR(PDX(a)+1, 0, 0)会得到0x400000(下一张页表的起始),减去PGSIZE(0x1000)后a变成0x3FF000,循环执行a += PGSIZE后,a就变成0x400000,直接跳到下一张页表的起始,完全跳过了0x100000到0x3FFFFF这4MB的无效地址。
核心结论
这行代码就是为了避免对空页表对应的大量未映射地址做无效遍历,用地址跳转的方式提升内存释放的效率,毕竟没必要对着一堆不存在的映射挨个检查。
内容的提问来源于stack exchange,提问作者brianmeowmeow
相关产品推荐
相关产品推荐

