实验三:虚实地址转换
实验之前
- 阅读实验指导三,可以结合
lab-3
分支的代码来理解。 - 本次的实验题将使用
lab-3+
分支,它包含了后面章节的内容,而我们不需要管那些部分。
实验题
原理:在
os/src/entry.asm
中,boot_page_table
的意义是什么?当跳转执行rust_main
时,不考虑缓存,硬件通过哪些地址找到了rust_main
的第一条指令?Click to showboot_page_table
是一个用二进制表示的根页表,其中包含两个 1GB 大页,分别是将虚拟地址0x8000_0000
至0xc000_0000
映射到物理地址0x8000_0000
至0xc000_0000
,以及将虚拟地址0xffff_ffff_8000_0000
至0xffff_ffff_c000_0000
映射到物理地址0x8000_0000
至0xc000_0000
。由于我们在
linker.ld
中指定了起始地址为0xffff_ffff_8020_0000
,操作系统执行文件会认为所有的符号都是在这个高地址上的。但是我们在硬件上只能将内核加载到0x8020_0000
开始的内存空间上,此时的pc
也会调转到这里。为了让程序能够正确跳转至高地址的
rust_main
,我们需要在entry.asm
中先应用内核重映射,即将高地址映射到低地址。但我们不可能在替换页表的同时修改pc
,此时pc
仍然处于低地址。所以,页表中的另一项(低地址的恒等映射)则保证程序替换页表后的短暂时间内,pc
仍然可以顺着低地址去执行内存中的指令。注:如果
boot_page_table
中不包含低地址恒等映射,程序可能仍然可以正常运行。这可能和硬件的缓存设计有关。但保险起见,应当保留这两个映射。执行
jal rust_main
时,硬件需要加载rust_main
对应的地址,大概是0xffff_ffff_802x_xxxx
。- 页表已经启用,硬件先从
satp
高位置读取内存映射模式,再从satp
低位置读取根页表页号,即boot_page_table
的物理页号 - 对于 Sv39 模式,页号有三级共 27 位。对于
rust_main
而言,一级页号是其 [30:38] 位,即 510。硬件此时定位到根页表的第 510 项 - 这一项的标志为 XWR,说明它指向一个大页而不是指向下一级页表;目标的页号为
0x8_0000
,即物理地址0x8000_0000
开始的区间;这一项的 V 位为 1,说明目标在内存中。因此,硬件寻址到页基址 + 页内偏移,即0x8000_0000 + 0x2x_xxxx
,找到rust_main
- 页表已经启用,硬件先从
分析:为什么
Mapping
中的page_tables
和mapped_pairs
都保存了一些FrameTracker
?二者有何不同?Click to show页表也是需要我们去分配页面来存储的。因此,
page_tables
存放了所有页表所用到的页面,而mapped_pairs
则存放了进程所用到的页面。分析:假设某进程需要虚拟地址 A 到物理地址 B 的映射,这需要操作系统来完成。那么操作系统在建立映射时有没有访问 B?如果有,它是怎么在还没有映射的情况下访问 B 的呢?
Click to show建立映射不需要访问 B,而只需要操作页表即可。不过,通常程序都会需要操作系统建立映射的同时向页面中加载一些数据。此时,尽管 A→B 的映射尚不存在,因为我们将整个可用物理内存都建立了内核映射,所以操作系统仍然可以通过线性偏移量来访问到 B。
实验:了解并实现时钟页面置换算法(或任何你感兴趣的算法),可以自行设计样例来比较性能
置换算法只需要修改
os/src/memory/mapping/swapper.rs
在
main.rs
中调用create_kernel_thread
来创建线程,你可以任意修改其中运行的函数,以达到测试效果