思维之海

——在云端,寻找我的星匙。

zCore研究

本文主要研究 zCore 的全貌和部分功能细节。

zCore 是一款教学操作系统。它是在 rCore 上的改进。

这部分计划工作被其它事情所耽搁了,可能很长一段时间内不再会有更新。

References

https://github.com/rcore-os/zCore (zCore 仓库)

https://rcore-os.github.io/zCore/zircon_object (zCore 代码文档)

https://fuchsia.dev/fuchsia-src/reference (Zircon 官方文档)

https://github.com/yunwei37/zcore_migration_notes(第二组记录)

开题报告

我希望的是轻量、快捷的参与方式。

目前 zCore-Tutorial 的内容还太少,我认为参与的前期准备工作较多,而且需要深度参与,这与我设想的参与方式不同,我可能没法抽出足够的时间做这件事情。

但是研究 zCore 的代码,写一些分析内容;添加注释文档和单元测试。这两项是比较轻量级的参与方式。这也是 zCore 文档与单元测试小组最开始的项目规划。

故目前的计划如下:

  • (25h)研究 zCore 中部分我感兴趣的代码,在增加我对 zCore 的理解的基础上,形成一些研究报告
    • 如果有可能的话,做一些轻量的改进,比如在zCore中添加zircon/linux的syscall
  • (25h)撰写注释文档,在没有足够说明的 zCore 代码段补充说明
  • (10h)单元测试,在测试不足的 zCore 代码段补充测试
  • (5h)对 zCore-Tutorial 独立性较强的章节做一些跟踪,提供轻量的PR
  • (5h)其它可能的参与方式
    • MaixPy / K210 相关的一些实验

计划失败。

工具链

Git Lens 版本历史查阅工具

在 VSCode 中搜索 Git Lens 查看 zCore 的版本历史修改记录。我记得以前使用 Intelligent IDEA 的时候也有类似的功能(但是所有版本是嵌入到一起的)。比如,对内核对象中的 mod.rs 进行早期版本的查阅:

函数调用图形化

draw.io 流程图

zCore 整体结构和设计模式

首先,从 Rust语言操作系统的设计与实现,王润基本科毕设论文,2019zCore操作系统内核的设计与实现,潘庆霖本科毕设论文,2020 可以了解到从 rCore 的设计到 zCore 的设计过程的全貌。

zCore 的整体结构

zCore 的整体结构/项目设计图如下:

zCore的设计主要有两个出发点:

  • 内核对象的封装:将内核对象代码封装为一个库,保证可重用
  • 硬件接口的设计:使硬件与内核对象的设计相对独立,只向上提供统一、抽象的API接口

项目设计从上到下,上层更远离硬件,下层更接近硬件。

zCore 设计的顶层是上层操作系统,比如 zCore、rCore、Zircon LibOS 和 Linux LibOS。在项目架构中,各版本的操作系统有部分公用代码。与 zCore 微内核设计实现相关的部分则主要是图中左侧蓝色线部分。

第二层,是 ELF 程序加载层(ELF Program Loader),包括 zircon-loader 和 linux-loader,其中封装了初始化内核对象、部分硬件相关的初始化、设定系统调用接口、运行首个用户态程序等逻辑,并形成一个库函数。zCore 在顶层通过调用 zircon-loader 库中的初始化逻辑,进入第一个用户态程序执行。

第三层,是系统调用实现层(Syscall Implementation),包括 zircon-syscall 和 linux-syscall,这一层将所有的系统调用处理例程封装为一个系统调用库,供上方操作系统使用。

第四层,利用硬件抽象层提供的虚拟硬件 API 进行内核对象(Kernel Objects)的实现,并且基于实现的各类内核对象,实现第三层各个系统调用接口所需要的具体处理例程。

第五层,是硬件抽象层(HAL,Hardware Abstraction Layer),这里对应的是 kernel-hal 模块。kernel-hal 将向上提供所有操作硬件需要的接口,从而使得硬件环境对上层操作系统透明化。

第六层,是对直接操作硬件的代码进行一层封装,对应模块为 kernel-hal-bare 和 kernel-hal-unix。kernel-hal 系列库仅仅负责接口定义,即将底层硬件/宿主操作系统的操作翻译为上层操作系统可以使用的形式。在这里,kernel-hal-bare 负责翻译裸机的硬件功能,而 kernel-hal-unix 则负责翻译类 Unix 系统的系统调用。

最底层是底层运行环境,包括 Bare Metal(裸机),Linux / macOS 操作系统。Bare Metal可以认为是硬件架构上的寄存器等硬件接口。

zCore 内核组件

zCore 内核运行时组件层次概况如下:

在zCore启动过程中,会初始化物理页帧分配器、堆分配器、线程调度器等各个组成部分。并委托 zircon-­loader 进行内核对象的初始化创建过程,然后进入用户态的启动过程开始执行。每当用户态触发系统调用进入内核态,系统调用处理例程将会通过已实现的内核对象的功能来对服务请求进行处理;而对应的内核对象的内部实现所需要的各种底层操作,则是通过 HAL 层接口由各个内核组件负责提供。

其中,VDSO(Virtual dynamic shared object)是一个映射到用户空间的 so 文件,可以在不陷入内核的情况下执行一些简单的系统调用。在设计中,所有中断都需要经过 VDSO 拦截进行处理,因此重写 VDSO 便可以实现自定义的对下层系统调用(syscall)的支持。Executor 是 zCore 中基于 Rust 的 async 机制的协程调度器。

在HAL接口层的设计上,还借助了 Rust 的能够指定函数链接过程的特性。即,在 kernel-­hal 中规定了所有可供 zircon­-object 库及 zircon-­syscall 库调用的虚拟硬件接口,以函数 API 的形式给出,但是内部均为未实现状态,并设置函数为弱引用链接状态。在 kernel­-hal-­bare 中才给出裸机环境下的硬件接口具体实现,编译 zCore 项目时、链接的过程中将会替换/覆盖 kernel-­hal 中未实现的同名接口,从而达到能够在编译时灵活选择 HAL 层的效果。

zCore 的设计模式

这里可以大致认为是与 rCore 设计的不同/改进之处。

Async 无栈协程

Async 机制是由 Rust 官方进行语法支持的异步编程机制,通过协程间的协作式调度提升性能。Rust 的无栈协程不仅能够减少线程切换的时间开销,还能减少多线程带来的内存占用;在占据更少的内存空间的情况下,保证并发性。其它主流编程语言也都提供了协程机制的支持,包括C#、C++、JavaScript、Python;不过 Async 机制几乎没有在 bare-metal(裸机)中应用。

Async 机制的特点是用同步风格编写异步代码,它是无栈协程(共享同一个栈,面向任务的轻量级线程),采用协作式调度(非抢占式调度)。可以认为 Async 机制是一个语法糖,其中的同步管理功能被封装得很好,从而在程序员看来是透明化得,所以在写法上与一般代码差不多,只需要略微增加一些语法修饰即可。

无栈协程的本质是一个状态机,将任务状态存储于对象中,每次运行都是一次函数调用,需要让出CPU时先更新对象状态,然后直接函数返回。这样每个任务都不需要保存自己的函数栈,无需上下文切换,任务切换开销很小。无栈协程适合有大量IO任务的场景,因此近年来在网络服务器中被大量使用。这样的机制很适用于高并发 IO 场景,但是因为本身是非抢占的,如果对性能有要求的话,可能必须要解决响应时间的问题。

一个简单的应用 Async 机制的 Rust 程序如下:(linux-syscall/src/task.rs

1
2
3
4
5
6
7
8
9
10
11
12
/// creates a child process of the calling process, similar to fork but wait for execve
pub async fn sys_vfork(&self) -> SysResult {
info!("vfork:");
let new_proc = Process::fork_from(self.zircon_process(), true)?;
let new_thread = Thread::create_linux(&new_proc)?;
new_thread.start_with_regs(GeneralRegs::new_fork(self.regs), self.spawn_fn)?;

let new_proc: Arc<dyn KernelObject> = new_proc;
info!("vfork: {} -> {}", self.zircon_process().id(), new_proc.id());
new_proc.wait_signal(Signal::SIGNALED).await; // wait for execve
Ok(new_proc.id() as usize)
}

首先需要在函数头部注明使用 Async 机制,即标注 ... async fn ...。在需要进行异步处理的代码,第10行处用 .await 来表示使用 Async 机制的语法。

HAL 硬件抽象层

硬件抽象层(Hardware Abstraction Layer,HAL)。