Skip to content

内核地址空间

Xv6 为每个进程维护一个页表,描述每个进程的用户地址空间,外加一个描述内核地址空间的单一页表。内核配置其地址空间的布局,以便在可预测的虚拟地址上访问物理内存和各种硬件资源。图 3 展示了这种布局如何将内核虚拟地址映射到物理地址。文件 kernel/memlayout.h 声明了 xv6 内核内存布局的常量。

QEMU 模拟了一台计算机,其 RAM(物理内存)从物理地址 0x80000000 开始,至少持续到 0x88000000,xv6 称之为 PHYSTOP。QEMU 模拟还包括 I/O 设备,如磁盘接口。QEMU 将设备接口作为内存映射的控制寄存器暴露给软件,这些寄存器位于物理地址空间中 0x80000000 以下。内核可以通过读/写这些特殊的物理地址与设备交互;这些读写操作是与设备硬件通信,而不是与 RAM 通信。第 4 章解释了 xv6 如何与设备交互。

内核使用“直接映射”来访问 RAM 和内存映射的设备寄存器;也就是说,将资源映射到与物理地址相等的虚拟地址。例如,内核本身位于虚拟地址空间和物理内存中的 KERNBASE=0x80000000。直接映射简化了读写物理内存的内核代码。例如,当 fork 为子进程分配用户内存时,分配器返回该内存的物理地址;fork 在将父进程的用户内存复制给子进程时,直接使用该地址作为虚拟地址。

有几个内核虚拟地址不是直接映射的:

  • 蹦床页。它被映射在虚拟地址空间的顶部;用户页表也有这个相同的映射。第 4 章讨论了蹦床页的作用,但我们在这里看到了页表的一个有趣用例;一个物理页(包含蹦床代码)在内核的虚拟地址空间中被映射了两次:一次在虚拟地址空间的顶部,一次是直接映射。

  • 内核栈页。每个进程都有自己的内核栈,它被映射在高地址,以便 xv6 可以在其下方留下一个未映射的保护页。保护页的 PTE 是无效的(即 PTE_V 未设置),因此如果内核溢出一个内核栈,它很可能会导致异常,内核会恐慌。没有保护页,溢出的栈会覆盖其他内核内存,导致不正确的操作。恐慌崩溃是更可取的。

虽然内核通过高内存映射使用其栈,但它们也可以通过直接映射的地址被内核访问。另一种设计可能只有直接映射,并在直接映射的地址上使用栈。然而,在这种安排中,提供保护页将涉及取消映射那些否则会引用物理内存的虚拟地址,而这些物理内存之后将难以使用。

内核使用权限 PTE_RPTE_X 映射蹦床和内核文本的页面。内核从这些页面读取和执行指令。内核使用权限 PTE_RPTE_W 映射其他页面,以便它可以读写这些页面中的内存。保护页的映射是无效的。