跳到主要内容

虚拟内存

rCore 采用简单分页的虚拟内存管理机制。本节基于 ch4 分支的 virtual-memory 标签进行调试,源码地址:

https://github.com/LearningOS/2025s-rcore-jiangshengdev/tree/virtual-memory

内核页表结构

我们先在 src/mm/memory_set.rs:277 行处设置断点并进入,分析内核态页表的构建流程。

new-kernel-code.webp new-kernel-code.webp

可以看到跳板映射为非恒等映射,.text 段为恒等映射。

new-kernel-print.webp new-kernel-print.webp

查看变量可知,内核内存集对应的页表根物理页号(root_ppn)为 0x83a5b。帧跟踪器共记录了 5 个物理页面:

  • 0x83a5b
  • 0x83a5c
  • 0x83a5d
  • 0x83a5e
  • 0x83a5f

new-kernel-debug.webp new-kernel-debug.webp

使用 GDB 分别查看这 5 个物理页号对应的 4 KiB 内存内容:

(gdb) x /512gx 0x83A5B000
(gdb) x /512gx 0x83A5C000
(gdb) x /512gx 0x83A5D000
(gdb) x /512gx 0x83A5E000
(gdb) x /512gx 0x83A5F000

由于输出冗长,此处仅绘制非 0x0 的项。

图中:

  • 灰色框表示寄存器或页表项数组;
  • 每个页表项数组包含 512 个元素,起始于对应物理页号映射的物理地址,占用 4 KiB;
  • 红色背景表示物理地址或寄存器名称,蓝色背景表示数组索引;
  • 绿色背景上部为存储值(下一级物理页号左移 10 位并附加标志位),下部为解析出的物理页号。

虽然此时 satp 寄存器尚未设置,但页表构建完成后会写入该寄存器。下图展示了完成后的值。

system-map.svg system-map.svg

可以看到 satp 寄存器指向物理页号 0x83a5b

跳板映射示例

以虚拟页号 0x7ffffff(三级索引 [511, 511, 511])映射到物理页号 0x0080201 为例:

  1. 第一级页表页(0x83a5b)下标 511 的表项地址 0x83a5bff8 指向 0x83a5c
  2. 第二级页表页(0x83a5c)下标 511 的表项地址 0x83a5cff8 指向 0x83a5d
  3. 第三级页表页(0x83a5d)下标 511 的表项地址 0x83a5dff8 解析得到物理页号 0x0080201

恒等映射示例

以虚拟页号 0x0080200(三级索引 [2, 1, 0])映射到相同物理页号 0x0080200 为例:

  1. 第一级页表页(0x83a5b)下标 2 的表项地址 0x83a5b010 指向 0x83a5e
  2. 第二级页表页(0x83a5e)下标 1 的表项地址 0x83a5e008 指向 0x83a5f
  3. 第三级页表页(0x83a5f)下标 0 的表项地址 0x83a5f000 解析得到物理页号 0x0080200

用户页表结构

在用户页表中,除了跳板映射以外,其余条目主要为帧映射,在映射时同时分配对应的物理内存。下图展示了应用程序 0 的页表组织结构:

user-map.svg user-map.svg

页表内存映射

本节示例在上游实现基础上新增了日志输出,可大致地展示内核态与用户态页表的内存映射详情。

内核内存映射

内核态大部分虚拟页面采用恒等映射,但跳板映射为静态映射,内核栈为帧映射:

[kernel] Hello, world! [DEBUG] [kernel] .rodata [0x8022a000, 0x80232000) [ INFO] [kernel] .data [0x80232000, 0x81a4a000) [ WARN] [kernel] boot_stack top=bottom=0x81a5a000, lower_bound=0x81a4a000 [ERROR] [kernel] .bss [0x81a5a000, 0x83a5b000) StackFrameAllocator { current: 0x83a5b, end: 0x88000, recycled: [], } Trampoline mapped: VPN:0x7ffffff -> PPN:0x0080201 Permissions: R | X [ INFO] .text [0x80200000, 0x8022a000) [ INFO] .rodata [0x8022a000, 0x80232000) [ INFO] .data [0x80232000, 0x81a4a000) [ INFO] .bss [0x81a4a000, 0x83a5b000) [ INFO] mapping .text section .text section mapping: VPN:0x0080200 -> PPN:0x0080200 ... VPN:0x0080229 -> PPN:0x0080229 42× Permissions: R | X [ INFO] mapping .rodata section .rodata section mapping: VPN:0x008022a -> PPN:0x008022a VPN:0x008022b -> PPN:0x008022b VPN:0x008022c -> PPN:0x008022c VPN:0x008022d -> PPN:0x008022d VPN:0x008022e -> PPN:0x008022e VPN:0x008022f -> PPN:0x008022f VPN:0x0080230 -> PPN:0x0080230 VPN:0x0080231 -> PPN:0x0080231 Permissions: R [ INFO] mapping .data section .data section mapping: VPN:0x0080232 -> PPN:0x0080232 ... VPN:0x0081a49 -> PPN:0x0081a49 6168× Permissions: R | W [ INFO] mapping .bss section .bss section mapping: VPN:0x0081a4a -> PPN:0x0081a4a ... VPN:0x0083a5a -> PPN:0x0083a5a 8209× Permissions: R | W [ INFO] mapping physical memory Physical memory section mapping: VPN:0x0083a5b -> PPN:0x0083a5b ... VPN:0x0087fff -> PPN:0x0087fff 17829× Permissions: R | W [kernel] back to world!

用户内存映射

用户态程序的页表主要采用帧映射。除了构建独立的用户页表(包含陷阱上下文映射),还会在内核页表中加入对应的内核栈映射:

num_app = 11 应用程序: 0 Trampoline (App) mapped: VPN:0x7ffffff -> PPN:0x0080201 Permissions: R | X .text section mapping: VPN:0x0000000 -> PPN:0x0083aa1 VPN:0x0000001 -> PPN:0x0083aa4 VPN:0x0000002 -> PPN:0x0083aa5 VPN:0x0000003 -> PPN:0x0083aa6 VPN:0x0000004 -> PPN:0x0083aa7 VPN:0x0000005 -> PPN:0x0083aa8 VPN:0x0000006 -> PPN:0x0083aa9 VPN:0x0000007 -> PPN:0x0083aaa VPN:0x0000008 -> PPN:0x0083aab Permissions: R | X | U .rodata section mapping: VPN:0x0000009 -> PPN:0x0083aac VPN:0x000000a -> PPN:0x0083aad VPN:0x000000b -> PPN:0x0083aae Permissions: R | U .data, .bss section mapping: VPN:0x000000c -> PPN:0x0083aaf VPN:0x000000d -> PPN:0x0083ab0 VPN:0x000000e -> PPN:0x0083ab1 VPN:0x000000f -> PPN:0x0083ab2 VPN:0x0000010 -> PPN:0x0083ab3 Permissions: R | W | U User stack section mapping: VPN:0x0000012 -> PPN:0x0083ab4 VPN:0x0000013 -> PPN:0x0083ab5 Permissions: R | W | U User sbrk section mapping: TrapContext section mapping: VPN:0x7fffffe -> PPN:0x0083ab6 Permissions: R | W Kernel stack section mapping: VPN:0x7fffffd -> PPN:0x0083ab7 VPN:0x7fffffe -> PPN:0x0083ab8 Permissions: R | W 应用程序: 1 Trampoline (App) mapped: VPN:0x7ffffff -> PPN:0x0080201 Permissions: R | X .text section mapping: VPN:0x0000000 -> PPN:0x0083abc VPN:0x0000001 -> PPN:0x0083abf VPN:0x0000002 -> PPN:0x0083ac0 VPN:0x0000003 -> PPN:0x0083ac1 VPN:0x0000004 -> PPN:0x0083ac2 VPN:0x0000005 -> PPN:0x0083ac3 VPN:0x0000006 -> PPN:0x0083ac4 VPN:0x0000007 -> PPN:0x0083ac5 VPN:0x0000008 -> PPN:0x0083ac6 Permissions: R | X | U .rodata section mapping: VPN:0x0000009 -> PPN:0x0083ac7 VPN:0x000000a -> PPN:0x0083ac8 VPN:0x000000b -> PPN:0x0083ac9 Permissions: R | U .data, .bss section mapping: VPN:0x000000c -> PPN:0x0083aca VPN:0x000000d -> PPN:0x0083acb VPN:0x000000e -> PPN:0x0083acc VPN:0x000000f -> PPN:0x0083acd VPN:0x0000010 -> PPN:0x0083ace Permissions: R | W | U User stack section mapping: VPN:0x0000012 -> PPN:0x0083acf VPN:0x0000013 -> PPN:0x0083ad0 Permissions: R | W | U User sbrk section mapping: TrapContext section mapping: VPN:0x7fffffe -> PPN:0x0083ad1 Permissions: R | W Kernel stack section mapping: VPN:0x7fffffa -> PPN:0x0083ad2 VPN:0x7fffffb -> PPN:0x0083ad3 Permissions: R | W

为了简洁,仅展示前两个应用程序的映射示例,后续应用结构类似。