如何循环遍历 xv6 中进程的所有页面 table 条目?
How to loop through all page table entries of a process in xv6?
我正在尝试遍历 xv6 中进程的所有页面。
我查看了这张图以了解它是如何工作的:
但我的代码正在获取:
unexpected trap 14 from cpu 0 eip 801045ff (cr2=0xdfbb000)
代码:
pde_t * physPgDir = (void *)V2P(p->pgdir);
int c = 0;
for(unsigned int i =0; i<NPDENTRIES;i++){
pde_t * pde = &physPgDir[i];
if(*pde & PTE_P){//page directory valid entry
int pde_ppn = (int)((PTE_ADDR(*pde)) >> PTXSHIFT);
pte_t * physPgtab = (void *)(PTE_ADDR(*pde));//grab 20 MSB for inner page table phys pointer;
// go through inner page table
for(unsigned int j =0;j<NPDENTRIES;j++){
pte_t * pte = &physPgtab[j];
if(*pte & PTE_P){//valid entry
c++;
unsigned int pte_ppn = (PTE_ADDR(*pte)) >> PTXSHIFT;//grab 20 MSB for inner page table phys pointer;
//do thing
}
}
}
}
这是在 proc.c 中的一些自定义函数中,在其他地方被调用。 p 是进程指针。
据我了解,cr3包含当前进程的物理地址。但是在我的例子中,我需要为给定的进程指针获取页面 table。 xv6 代码似乎在 cr3 中加载 V2P(p->pgdir)。这就是我尝试获取 V2P(p->pgdir) 的原因。但是,陷阱发生在取消引用 pde 之前。这意味着那里有问题。我不应该使用实际地址吗?
编辑:正如 Brendan 回答的那样,虚拟地址 p->pgdir 应该被解除引用。此外,页面目录中的 PPN 也应通过 P2V 进行转换,以正确地取消引用到页面 table。
如果以后其他人也对 xv6 的这方面感到困惑,我希望能有所帮助。
处理分页的黄金法则是“永远不要在任何类型的指针中存储物理地址”。原因是:
a) 它们不是虚拟地址,不能取消引用,因此如果您尝试使用物理地址作为指针,最好通过确保出现编译时错误来使错误明显。
b) 在某些情况下,物理地址与虚拟地址的大小不同(例如 80x86 中的“PAE 分页”,其中虚拟地址仍然是 32 位,但物理地址可能高达 52 位);而且它更好(为了便携性——例如,这样在某些时候可以更容易地将 PAE 支持添加到 XV6)。
考虑到这一点,您的第一行代码是一个明显的错误(它打破了“黄金法则”)。它应该是 pde_t physPgDir = V2P(p->pgdir);
或 pde_t * pgDir = p->pgdir;
。我会让你弄清楚哪个(因为我怀疑这是家庭作业,而且我相信通过坚持“黄金法则”你会解决你自己的问题)。
我正在尝试遍历 xv6 中进程的所有页面。
我查看了这张图以了解它是如何工作的:
但我的代码正在获取:
unexpected trap 14 from cpu 0 eip 801045ff (cr2=0xdfbb000)
代码:
pde_t * physPgDir = (void *)V2P(p->pgdir);
int c = 0;
for(unsigned int i =0; i<NPDENTRIES;i++){
pde_t * pde = &physPgDir[i];
if(*pde & PTE_P){//page directory valid entry
int pde_ppn = (int)((PTE_ADDR(*pde)) >> PTXSHIFT);
pte_t * physPgtab = (void *)(PTE_ADDR(*pde));//grab 20 MSB for inner page table phys pointer;
// go through inner page table
for(unsigned int j =0;j<NPDENTRIES;j++){
pte_t * pte = &physPgtab[j];
if(*pte & PTE_P){//valid entry
c++;
unsigned int pte_ppn = (PTE_ADDR(*pte)) >> PTXSHIFT;//grab 20 MSB for inner page table phys pointer;
//do thing
}
}
}
}
这是在 proc.c 中的一些自定义函数中,在其他地方被调用。 p 是进程指针。 据我了解,cr3包含当前进程的物理地址。但是在我的例子中,我需要为给定的进程指针获取页面 table。 xv6 代码似乎在 cr3 中加载 V2P(p->pgdir)。这就是我尝试获取 V2P(p->pgdir) 的原因。但是,陷阱发生在取消引用 pde 之前。这意味着那里有问题。我不应该使用实际地址吗?
编辑:正如 Brendan 回答的那样,虚拟地址 p->pgdir 应该被解除引用。此外,页面目录中的 PPN 也应通过 P2V 进行转换,以正确地取消引用到页面 table。 如果以后其他人也对 xv6 的这方面感到困惑,我希望能有所帮助。
处理分页的黄金法则是“永远不要在任何类型的指针中存储物理地址”。原因是:
a) 它们不是虚拟地址,不能取消引用,因此如果您尝试使用物理地址作为指针,最好通过确保出现编译时错误来使错误明显。
b) 在某些情况下,物理地址与虚拟地址的大小不同(例如 80x86 中的“PAE 分页”,其中虚拟地址仍然是 32 位,但物理地址可能高达 52 位);而且它更好(为了便携性——例如,这样在某些时候可以更容易地将 PAE 支持添加到 XV6)。
考虑到这一点,您的第一行代码是一个明显的错误(它打破了“黄金法则”)。它应该是 pde_t physPgDir = V2P(p->pgdir);
或 pde_t * pgDir = p->pgdir;
。我会让你弄清楚哪个(因为我怀疑这是家庭作业,而且我相信通过坚持“黄金法则”你会解决你自己的问题)。