Xv6中P2V、V2P宏背后的机制是什么

What's the mechanism behind P2V, V2P macro in Xv6

我知道虚拟地址如何变成物理地址有一个映射机制。

就像下面这样,一个线性地址包含三部分

  1. 页面目录索引
  2. 页Table索引
  3. 偏移量

插图如下:

现在,当我在 memorylayout.h

中查看 Xv6 的源代码时
#define V2P(a) (((uint) (a)) - KERNBASE)
#define P2V(a) (((void *) (a)) + KERNBASE)

#define V2P_WO(x) ((x) - KERNBASE)    // same as V2P, but without casts
#define P2V_WO(x) ((x) + KERNBASE)    // same as P2V, but without casts

V2P或P2V不做地址转换怎么能正常工作呢?

V2P 和 P2V 宏的功能并没有您想象的那么多。它们只是减去并添加标记为 2 GB 的 KERNBASE 常量。

看来你对MMU的硬件映射机制理解正确。 映射规则由每个进程页表保存。这些表构成了进程的虚拟 space.

具体来说,在 XV6 中,进程的虚拟 space 结构正在构建(通过适当的映射)如下:virtual address space layout

如上图所示,XV6 专门构建了进程的虚拟地址space,以便 2GB - 4GB 虚拟地址映射到 0 到 PHYSTOP 物理地址(分别)。正如XV6官方评论中解释的那样:

Xv6 includes all mappings needed for the kernel to run in every process’s page table; these mappings all appear above KERNBASE. It maps virtual addresses KERNBASE:KERNBASE+PHYSTOP to 0:PHYSTOP.

还说明了做出此决定的动机:

One reason for this mapping is so that the kernel can use its own instructions and data. Another reason is that the kernel sometimes needs to be able to write a given page of physical memory, for example when creating page table pages; having every physical page appear at a predictable virtual address makes this convenient.

换句话说,因为内核使所有页表将2GB虚拟映射到0物理(直到PHYSTOP),我们可以很容易地找到2GB以上的虚拟地址的物理地址。 例如:虚拟地址0x10001000的物理地址很容易找到:它是0x00001000我们只是减去2GB,因为我们是这样映射的。

这"workaround"可以帮助内核轻松地进行转换,例如,用于构建和操作页表(需要计算物理地址并写入内存)。

当然上面的"workaround"不是免费的,因为这让我们浪费了宝贵的虚拟地址space(2GB),因为现在每个物理地址至少已经有1个虚拟地址了。即使我们永远不会使用它。这样真正的进程就只剩下整个虚拟地址剩下的2GB了(一共是4GB,因为我们是用32bit来统计地址的)。这个在XV6官方解说中也有解释:

A defect of this arrangement is that xv6 cannot make use of more than 2 GB of physical memory

我建议您在 "Process address space" header 下的 XV6 评论中阅读更多关于这种方式的信息。 XV6 Commentary