如何循环遍历 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;。我会让你弄清楚哪个(因为我怀疑这是家庭作业,而且我相信通过坚持“黄金法则”你会解决你自己的问题)。