本文主要摘选自 stlankes 发表于 2021 年 3 月的文章 The RustyHermit Unikernel。
RustyHermit,是一个 Unikernel 应用,它完全是由 Rust 开发的。Unikernels 是直接将内核作为库方式包含的应用程序映像,因此不需要安装操作系统(OS)。它们通常用于构建典型云应用,或者基础设施建设的核心虚拟化环境。
这篇文章是我们 Showcase 系列的一部分,在这里,我们展示了 Rust 操作系统生态系统中有趣的项目。如果您愿意,可以通过创建 PR 来添加您自己的项目。
常见的虚拟化环境,是基于经典虚拟机的。在这种情况下,将模拟或虚拟化完整的机器,并在主机和来客户机上运行通用操作系统:
这项技术已经在 VMware、Hyper-V 等虚拟产品中得到了广泛应用。然而,它引入了额外的开销,特别是在内存消耗和性能方面。
通用虚拟机的另一种设计方法是:从操作系统级别虚拟化,其内核,允许存在多个独立的用户空间实例。这些孤立实例即称为容器。一个典型的代表是 LXC 或 Docker,与普通虚拟机相比,它的开销更小。但是,进程之间的隔离性较弱,可能提供的安全性较差。
通常只有一个应用程序,如 web 服务器,在容器或虚拟机中运行。在这种情况下,单核是一个很有吸引力的解决方案。内核作为静态库,提供并链接到应用程序。由于映像直接包含 OS 内核,unikernel 可以直接在虚拟机中引导运行,并且不需要在 VM 中包含 Linux 内核,也不需要包含用户区的典型软件堆栈。
unikernel 不提供传统意义上的系统调用,因为所有操作都是以内核的特殊级别运行的。通常,unikernel 中,通过系统调用完成的操作,是通过公共函数调用提供的。乍一看,这听起来比以前的方法更不安全。但是,这些内核是在虚拟机中运行,这将应用程序与实际系统隔离开来。此外,利用通用编译器分析方法检查软件栈的完整性,甚至可以删除不需要的组件,减少应用程序的占用。
比较流行的 unikernel,是诸如 MirageOS 和 Unikraft 这样的内核。其中 MirageOS 是用 OCaml 语言开发的,而 Unikraft 仍然是使用经典的 C 语言。与这些内核不同,RustyHermit 完全用 Rust 编写,以受益 Rust 的高性能和安全性。
原则上,每个现有的 Rust 应用,都可以构建于 RustyHermit 之上。然而,unikernel 是一个单任务操作系统。因此,缺少对系统调用 fork
,以及进程间通信的支持。此外,还缺少一个经典的 C 语言库,它通常用作操作系统的接口。目前,那些绕过标准运行时,并试图直接与操作系统通信的 Rust crate,需要做对应修改。然而,绝大部分应用程序并不依赖于这些特性,所以可在 RustyHermit 上正常运行。
单核,意味着可以高度优化。例如,我们优化了 RustyHermit 的网络堆栈。RustyHermit 使用 smolcp 作为网络堆栈,smolcp 完全是用 Rust 编写的。作为客户机和主机操作系统之间的接口,我们使用 Virtio,Virtio 包含在 KVM 的准虚拟化驱动程序之中,被广泛用于虚拟化 Linux 环境。
下图比较了 Linux 和 RustyHermit 的性能,两者都作为 guest 用户,在基于 Linux 主机系统上的虚拟机中运行:
注:如果要测试实例,请注意下述各自配置文件和配置细节。
为了完整演示一个构建 RustyHermit 应用的示例,让我们创建一个新的 cargo(中文文档)项目:
cargo new hello_world
cd hello_world
RustyHermit 目前需要 Rust 工具链的 nightly
版本。为了简化工作流程,我们建议按如下方式创建和配置工具链,以定义所需的组件,并测试 nightly
编译器的版本:
Rust 工具链的安装和配置,以及国内镜像,请参与参考文档:Rust 环境配置(Linux、macOS、Windows)、配置 Rust 工具链的国内源。
[toolchain]
channel = "nightly-2020-12-23"
components = [ "rustfmt", "rust-src", "llvm-tools-preview"]
targets = [ "x86_64-unknown-hermit" ]
配置文件指定所需的组件,以及要使用的 nightly
编译器的版本。
RustyHermit 的构建目标是 target x86_64-unknown-hermit
,其是 Rust 支持平台的一部分,但是不属于第 1 层平台,所以我们需要做编译配置。在 .cargo/config
中配置:
[unstable]
build-std = ["std", "core", "alloc", "panic_abort"]
[build]
target = "x86_64-unknown-hermit"
为了将操作系统库绑定到应用程序,我们必须将 crate hermit-sys 添加到文件中的依赖项中。在 Cargo.toml
中配置:
# Cargo.toml
[target.'cfg(target_os = "hermit")'.dependencies]
hermit-sys = "0.1.*"
features = ["smoltcp"]
如果您的应用程序需要建立 TCP 连接,则需要 smoltcp crate。
此实例结果,是一个 64 位的可执行链接格式(ELF)。要在公共的虚拟机中启动此应用程序,需要一个加载程序,它初始化处理器,并启动应用程序。我们在 GitHub 上提供了一个简单的加载程序。注意 makefile
,用于构建加载程序,是项目的一部分。之后,在 VM 中,Qemu 可用于启动 RustyHermit,如下所示:
qemu-system-x86_64 -display none -smp 1 -m 64M -serial stdio
-kernel path_to_loader/rusty-loader
-initrd path_to_app/app -cpu qemu64,apic,fsgsbase,rdtscp,xsave,fxsr
为了提高性能,可以使用 KVM 进行处理器的虚拟化扩展。
将来,RustyHermit 计划稳定与硬件的接口。例如,Virtio-fs 的支持。此外,与 Rust 标准库的集成还没有最终确定……
此项目还是起步阶段,github 星星数 240 左右。
谢谢您的阅读!