5.9 KiB
2018操作系统专题训练
实验3:实现和测试报告
计53 王润基 2015011279
2018.10.25
实验目标
基于RustOS,参考sv6完成多核实现和优化。
分为以下三个子任务:
- 实现x86_64和RISCV下的多核启动和通信
- 拓展线程管理模块,使之支持多核调度
- 学习sv6进行多核优化
实验进展
基本完成前两项子任务,即:实现了多核线程调度执行。
具体的任务节点是:
- 到10月19日,我完成了x86下的多核启动和IPI。与此同时,王纪霆完成了riscv32下的多核启动,并针对编译器原子操作缺失问题给出了临时解决方案。我合并了他的成果,至此多核的底层支持实现完毕。
- 到10月25日,我完成了线程管理模块针对多处理机模型的重构,原来正常的用户程序都能在多核环境下正确执行。
实现方案
x86的多核启动
实际上这部分工作已经在上学期OS课大作业中实现完毕。不过由于之后替换了x86下的bootloader,多核启动特性被暂时关闭。因此实际的任务是:把原来Kernel中的多核启动代码,移植到新bootloader中。
简单描述多核启动流程:
-
将其它核的启动代码放置于物理地址0x8000处
这个靠修改linker.ld,在链接阶段完成。物理地址必须4K对齐。
-
在主核初始化完毕后,调用一段操作LocalAPIC的代码(借用自xv6),向其它核发送启动IPI,让它从0x8000开始执行。主核需依次启动其它核,因为它们使用相同的栈空间,不能同时执行。
-
启动后,首先进行boot,在16位下设置段寄存器、页表、开启LongMode,直接进入64位,跳到Rust代码,再跳到Kernel入口点。
目前还未实现的特性是:
-
应该首先读取ACPI中的LocalAPIC信息,确定核的个数。
我原本计划使用RustOSDev的ACPI库,但它仍在开发中,功能尚不完全。
x86的中断管理
在原来的单核环境中,系统使用PIC中断控制器和PIT时钟。
而在多核环境中,它们被IOAPIC和APIC Timer替代。
为此,我参考了xv6/sv6/Redox在这方面的实现,其中xv6实现最为简单,sv6进行了更好的对象包装,Redox提供了Rust下实现的参考。我发现这部分功能相对独立,而还没有人发布过Rust crate实现APIC功能,因此我自己实现了一个APIC crate,综合参考了以上三个项目的代码。并将它用在了bootloader和kernel中。
合并RV工作
riscv32下的多核启动由王纪霆同学完成。我之后为它开启了IPI。
由于Rust编译器对riscv支持还不完全,不能生成原子指令,且核心库中的原子操作也没有为rv适配,具体体现为:rv下只有32位数据的原子操作,因此没有AtomicBool;此外即使用AtomicUsize实现的Mutex也不能正常工作,陈秋昊同学经过实验推测是Rust编译器内部实现的问题。
针对上述问题,王纪霆的解决方案是修改Kernel中Mutex的实现。而我在考虑直接修改核心库的实现,这样上层不用做修改,spin库也能正常使用。不过这一想法还没付诸实施。
多核线程管理
以上几个任务完成了平台相关的工作,接下来的任务都是平台无关的。
这部分主要工作是重构了process模块,使之支持多处理机模型。
在原来的设计中,用一个Processor类维护所有线程的状态,同时负责线程切换,对外提供exit/wait/sleep等控制接口。它是全局唯一的,用一个大锁包起来。
在新的设计中,线程状态和实际执行被分开。其中线程状态由ProcessManager类管理,另外的Processor类负责线程的实际执行。前者是全局唯一的,后者是CPU核的抽象,每个核对应一个。由于它们都需要定义为全局变量,因此对外接口都是&self,内部用SpinLock或UnsafeCell维护可变状态。
参考xv6/ucore的设计,每个CPU核在初始化完毕后都进入自己的“调度线程”,无限重复以下操作:
- 从ProcessManager中取出一个可执行线程的上下文(委托内部的Scheduler决定选哪个)
- 执行上下文切换运行该线程
- 线程运行时(主动地,或在时钟中断时被动地)调用yield切换回调度线程
- 将运行完的线程上下文交还ProcessManager
上述内容实现后,就得到了一个最基础的线程执行框架:
- 每个进程有3个状态:Ready等待执行,Running正在执行,Sleep不可执行
- 若干处理机并发地从一个公共线程池里取出、执行、放回
注意这个实现不包括:
- 维护线程间的父子关系(所有线程是平级的)
- 维护线程间的等待关系(等待和唤醒应该由等待队列完成)
这部分工作的前期大量时间投入在构思设计上:如何界定类的指责和它们的交互关系?更具体的——哪里用锁?哪里可变?每个需求如何实现?受制于Rust类型系统和安全机制的约束,要设计一个编译通过的方案并不容易。
前期设计断断续续用了几天时间,而编写代码只用了一天,而其中相当一部分时间在与编译器作斗争。不过在编译通过后,仅修复了几个明显的Bug就能正常运行了!完全没有数据竞争问题!不得不承认Rust语言在处理并发问题上有很大优势。
未来计划
- 在HiFive开发版上运行RustOS
- 更深入研究sv6,移植其中的多核优化特性
- 完善系统调用,争取能跑起来sv6的用户程序,方便进行性能对比