虚拟内存
rCore 采用简单分页的虚拟内存管理机制。本节基于 ch4
分支的 virtual-memory
标签进行调试,源码地址:
https://github.com/LearningOS/2025s-rcore-jiangshengdev/tree/virtual-memory
内核页表结构
我们先在 src/mm/memory_set.rs:277
行处设置断点并进入,分析内核态页表的构建流程。
可以看到跳板映射为非恒等映射,.text
段为恒等映射。
查看变量可知,内核内存集对应的页表根物理页号(root_ppn
)为 0x83a5b
。帧跟踪器共记录了 5 个物理页面:
0x83a5b
0x83a5c
0x83a5d
0x83a5e
0x83a5f
使用 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
寄存器尚未设置,但页表构建完成后会写入该寄存器。下图展示了完成后的值。
可以看到 satp
寄存器指向物理页号 0x83a5b
。
跳板映射示例
以虚拟页号 0x7ffffff
(三级索引 [511, 511, 511]
)映射到物理页号 0x0080201
为例:
- 第一级页表页(
0x83a5b
)下标 511 的表项地址0x83a5bff8
指向0x83a5c
; - 第二级页表页(
0x83a5c
)下标 511 的表项地址0x83a5cff8
指向0x83a5d
; - 第三级页表页(
0x83a5d
)下标 511 的表项地址0x83a5dff8
解析得到物理页号0x0080201
。
恒等映射示例
以虚拟页号 0x0080200
(三级索引 [2, 1, 0]
)映射到相同物理页号 0x0080200
为例:
- 第一级页表页(
0x83a5b
)下标 2 的表项地址0x83a5b010
指向0x83a5e
; - 第二级页表页(
0x83a5e
)下标 1 的表项地址0x83a5e008
指向0x83a5f
; - 第三级页表页(
0x83a5f
)下标 0 的表项地址0x83a5f000
解析得到物理页号0x0080200
。
用户页表结构
在用户页表中,除了跳板映射以外,其余条目主要为帧映射,在映射时同时分配对应的物理内存。下图展示了应用程序 0 的页表组织结构:
页表内存映射
本节示例在上游实现基础上新增了日志输出,可大致地展示内核态与用户态页表的内存映射详情。
内核内存映射
内核态大部分虚拟页面采用恒等映射,但跳板映射为静态映射,内核栈为帧映射:
[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
为了简洁,仅展示前两个应用程序的映射示例,后续应用结构类似。