Jailhouse
作为 Linux Journal 的读者,您可能已经知道 Linux 拥有丰富的虚拟化生态系统。KVM 是事实上的标准,而 VirtualBox 则广泛用于桌面虚拟化。资深人士应该还记得 Xen(顺便说一句,它仍然状态良好),还有 VMware(它不是免费的,但也运行在 Linux 上)。此外,还有许多不太知名的虚拟机监控器,如教育性质的 lguest 或业余爱好者 Xvisor。在如此拥挤的环境中,新来者还有立足之地吗?
创建另一个基于 Linux 的“通用”虚拟机监控器可能没有太多意义(除了只是为了好玩,你知道的)。但是,有些特定的用例是通用解决方案无法很好地解决的。其中一个领域是实时虚拟化,它经常用于工业自动化、医疗、电信和高性能计算。在这些应用中,将整个 CPU 或其核心专门用于裸机运行的软件(没有底层操作系统)是一种满足严格截止期限要求的方法。虽然可以将 KVM 实例绑定到处理器核心并将 PCI 设备直通给 guest 虚拟机,但测试表明,最坏情况下的延迟可能高于某些实际需求(请参阅“资源”部分)。
与往常的自由软件一样,情况正在随着时间的推移而好转,但还有另一件事——安全性。敏感软件系统要经过严格的认证(如通用准则)甚至形式化验证程序。如果您希望它们虚拟化运行(例如,为了整合目的),虚拟机监控器必须将它们与不可认证的工作负载隔离。这意味着虚拟机监控器本身必须足够小;否则,它最终可能会比它隔离的软件更大(也更“可疑”),从而破坏了整个隔离的想法。
因此,看起来对于实时和可认证的工作负载,轻量级(对于实时阵营)、小型且简单(对于安全人员)的开源 Linux 友好型虚拟机监控器仍有一定空间。这就是 Jailhouse 发挥作用的地方。
新来者Jailhouse 诞生于西门子,自 2013 年 11 月以来一直作为一个自由软件项目(GPLv2)进行开发。去年 8 月,Jailhouse 0.1 发布给公众。Jailhouse 还很年轻,与其说是一个随时可用的工具,不如说更像是一个研究项目,但现在是熟悉它并为在生产环境中遇到它做好准备的好时机。
从技术角度来看,Jailhouse 是一个静态分区虚拟机监控器,它裸机运行,但与 Linux 紧密合作。这意味着 Jailhouse 不会模拟您没有的资源。它只是将您的硬件分成称为“单元 (cells)”的隔离隔间,这些隔间完全专用于称为“囚犯 (inmates)”的 guest 虚拟机软件程序。其中一个单元运行 Linux 操作系统,被称为“根单元 (root cell)”。其他单元在创建时从根单元借用 CPU 和设备(图 1)。

图 1. Linux 裸机运行 (a) 和在 Jailhouse 虚拟机监控器 (b) 下与实时应用程序并排运行的可视化。(图片来自 Yulia Sinitsyna;Tux 图片来自 Larry Ewing。)
除了 Linux,Jailhouse 还支持裸机应用程序,但它无法未经修改地运行通用操作系统(如 Windows 或 FreeBSD)。如前所述,如果您需要这样做,还有很多其他选择。有一天,Jailhouse 也可能支持在根单元中运行 KVM,从而提供两全其美的方案。
如前所述,Jailhouse 与 Linux 紧密合作,并依赖它进行硬件引导、虚拟机监控器启动和执行管理任务(如创建新单元)。引导在这里非常重要,因为对于现代计算机来说,这是一项相当复杂的任务,而在 Jailhouse 中实现它会使其复杂得多。话虽如此,Jailhouse 并没有像 KVM(它是一个内核模块)那样与内核融合。它作为固件镜像加载(与 Wi-Fi 适配器加载其固件 blob 的方式相同),并驻留在您应该在 Linux 启动时保留的专用内存区域中。Jailhouse 的内核模块(jailhouse.ko,也称为“驱动程序”)加载固件并创建 /dev/jailhouse 设备,Jailhouse 用户空间工具使用该设备,但它不包含任何虚拟机监控器逻辑。
Jailhouse 是异步多处理 (AMP) 架构的一个示例。与传统的对称多处理 (SMP) 系统相比,Jailhouse 中的 CPU 核心不会被平等对待。核心 0 和 1 可以运行 Linux 并访问 SATA 硬盘驱动器,而核心 2 运行一个只能访问串行端口的裸机应用程序。由于 Jailhouse 可以运行的大多数计算机都具有共享的 L2/L3 缓存,这意味着存在缓存抖动的可能性。要理解为什么会发生这种情况,请考虑 Jailhouse 为不同的囚犯将相同的 guest 物理内存地址 (GPA) 映射到不同的主机(或真实)物理地址。如果两个囚犯偶尔在同一个 L2/L3 缓存行中具有相同的 GPA(自然包含不同的数据),由于缓存关联性,它们会相互干扰对方的工作并降低性能。这种影响尚未测量,Jailhouse 目前没有专门的手段来缓解它。但是,希望对于许多应用程序来说,这种性能损失不会是至关重要的。
现在您已经有足够的背景知识来理解 Jailhouse 是什么(以及它不是什么),我希望您有兴趣了解更多信息。让我们看看如何在您的系统上安装和运行它。
获取最新版本有时您可能需要最新的 KVM 和 QEMU 才能试用 Jailhouse。KVM 是内核的一部分,仅仅为了尝试一些新软件而更新关键系统组件可能显得有些杀鸡用牛。幸运的是,还有另一种方法。
kvm-kmod 是一个从一个内核获取 KVM 模块并为另一个内核编译它们的工具,它通常用于为当前内核构建最新的 KVM。构建过程在 README 中详细说明,但简而言之,您克隆存储库,初始化一个子模块(它是 KVM 的源代码),然后运行 configure 脚本,然后运行 make
。当模块准备就绪后,只需 insmod
它们来代替您的发行版提供的模块(不要忘记先卸载那些模块)。如果您希望更改永久生效,请运行 make modules_install
。kvm-kmod 可以从您指向的任何位置获取 KVM 源代码,但默认值通常就足够了。
编译 QEMU 更容易,但更耗时。它遵循通常的 configure && make
过程,并且不需要系统范围安装(这对于软件包管理器来说是友好的)。只需在文本示例中使用 /path/to/qemu/x86_64-softmmu/qemu-system-x86_64
代替普通的 qemu-system-x86_64
即可。
尽管现在已经发布了 0.1 版本,但 Jailhouse 仍然是一个年轻的项目,正在快速开发中。由于同样的原因,您不太可能在您的发行版存储库中找到它,因此获取 Jailhouse 的首选方法是从 Git 构建它。
要运行 Jailhouse,您需要一个最新的多核支持 VT-x 的 Intel x86 64 位 CPU 和一个支持 VT-d 的主板。在您阅读本文时,可能也支持 64 位 AMD CPU 甚至 ARM(v7 或更高版本)。代码已经在这里(请参阅“资源”部分),但尚未集成到主线中。建议至少 1GB 的 RAM,对于我在下面讨论的嵌套设置,甚至需要更多。在软件方面,您需要常用的开发工具(make、GCC、Git)和 Linux 内核的头文件。
目前在真实硬件上运行 Jailhouse 并不简单,所以如果您只想试用一下,有一个更好的替代方案。如果您满足 CPU 要求,虚拟机监控器应该可以在 KVM/QEMU 下良好运行。这被称为嵌套设置。Jailhouse 依赖于一些尖端功能,因此您至少需要 Linux 3.17 和 QEMU 2.1 才能使一切顺利运行。除非您使用的是滚动发布发行版,否则这可能是一个问题,因此您可能需要自己编译这些工具。有关更多信息,请参阅“获取最新版本”侧边栏,我建议您即使您有幸预先打包了所需的版本,也应该看一下它。Jailhouse 在不断发展,在您阅读本文时,可能需要尚未发布的功能和修复程序。
确保您在 KVM 中启用了嵌套模式。kvm-intel 和 kvm-amd 内核模块都接受 nested=1
参数,该参数正是为此负责的。您可以手动设置它,在 modprobe 命令行上(不要忘记先卸载以前的模块实例)。或者,在 /etc/modprobe.d 下的新文件中添加 options kvm-intel nested=1
(或类似的 kvm-amd 行)。
您还应该为 Jailhouse 和囚犯保留内存。为此,只需将 memmap=66M$0x3b000000
添加到内核命令行即可。对于一次性使用,请从 GRUB 菜单执行此操作(按 e,编辑命令行,然后按 F10)。要使更改永久生效,请在 QEMU guest 虚拟机端编辑 /etc/default/grub 中的 GRUB_CMDLINE_LINUX
变量,并使用 grub-mkconfig
重新生成配置。
现在,制作您最喜欢的发行版的 JeOS 版本。您可以使用 SUSE Studio、ubuntu-vm-builder 和类似工具生成一个,或者只是以通常的方式自己安装一个最小系统。建议主机和 QEMU 内部使用相同的内核。现在,运行虚拟机,如下所示(假设是 Intel CPU):
qemu-system-x86_64 -machine q35 -m 1G -enable-kvm -smp 4
↪-cpu kvm64,-kvm_pv_eoi,-kvm_steal_time,-kvm_asyncpf,
↪-kvmclock,+vmx,+x2apic -drive
↪file=LinuxInstallation.img,id=disk,if=none
↪-virtfs local,path=/path/to/jailhouse,
↪security_model=passthrough,mount_tag=host
↪-device ide-hd,drive=disk -serial stdio
↪-serial file:com2.txt
注意,我启用了 9p (-virtfs) 以从 QEMU guest 虚拟机端访问主机文件系统;/path/to/jailhouse 是您现在要编译 Jailhouse 的位置。cd
到此目录并运行
git clone git@github.com:siemens/jailhouse.git jailhouse
cd jailhouse
make
现在,切换到 guest 虚拟机并挂载 9p 文件系统(例如,使用 mount -t 9p host /mnt
)。然后,cd
到 /mnt/jailhouse 并执行
sudo make firmware_install
sudo insmod jailhouse.ko
这会将您构建的 Jailhouse 二进制镜像复制到 /lib/firmware 并插入 Jailhouse 驱动程序模块。现在您可以使用以下命令启用 Jailhouse:
sudo tools/jailhouse enable configs/qemu-vm.cell
当命令返回时,键入 dmesg | tail
。如果您看到“The Jailhouse is opening.”消息,则表示您已成功启动虚拟机监控器,并且您的 Linux guest 虚拟机现在在 Jailhouse 下运行(Jailhouse 本身在 KVM/QEMU 下运行)。如果您收到错误,则表明您的 CPU 缺少某些必需的功能。如果 guest 虚拟机挂起,则很可能是因为您的主机内核或 QEMU 对于 Jailhouse 来说不够新,或者 qemu-vm 单元配置有问题。Jailhouse 将其所有消息发送到串行端口,而 QEMU 只是将它们打印到启动它的终端(图 2)。查看消息以了解哪个资源(I/O 端口、内存等)导致了问题,并继续阅读以了解 Jailhouse 配置的详细信息。

图 2. 典型的配置问题:Jailhouse 捕获来自根单元的“禁止”操作。
配置和囚犯创建 Jailhouse 配置文件并不简单。由于代码库必须保持小巧,因此在其他虚拟机监控器中自动发生的大部分逻辑必须在此处手动完成(尽管在 Jailhouse 附带的工具的帮助下)。与 libvirt 或 VirtualBox XML 相比,Jailhouse 配置文件非常详细且相当底层。配置目前以编译为原始二进制文件的纯 C 文件形式表示(在源代码的 configs/ 下找到);但是,未来版本可能会使用另一种格式(如 DeviceTree)。
大多数情况下,您不需要从头开始创建单元配置,除非您编写了一个全新的囚犯,或者希望虚拟机监控器在您的特定硬件上运行(请参阅“真实环境中的 Jailhouse”侧边栏)。
单元配置文件包含诸如虚拟机监控器基地址(它应该在您之前使用 memmap=
保留的区域内)、分配给单元的 CPU 掩码(对于根单元,它是 0xff 或系统中的所有 CPU)、内存区域列表以及此单元对其拥有的权限、I/O 端口位图(0 将端口标记为单元可访问)和 PCI 设备列表之类的信息。
每个 Jailhouse 单元都有自己的配置文件,因此您将有一个用于根单元的配置文件,描述 Jailhouse 在其上执行的平台(如您在上面看到的 qemu-vm.c),以及几个用于每个正在运行的单元的其他配置文件。囚犯可以共享一个配置文件(因此共享一个单元),但那时只有一个囚犯会在给定时间处于活动状态。
为了启动一个囚犯,您需要先创建它的单元
sudo tools/jailhouse cell create configs/apic-demo.cell
apic-demo.cell 是 Jailhouse 附带的单元配置文件(我还假设您仍然使用前面描述的 QEMU 设置)。此单元不使用任何 PCI 设备,但在更复杂的情况下,建议在通过此命令将设备移动到单元之前卸载 Linux 驱动程序。
现在,可以将囚犯镜像加载到内存中
sudo tools/jailhouse cell load apic-demo
↪inmates/demos/x86/apic-demo.bin -a 0xf0000
Jailhouse 将所有囚犯视为不透明的二进制文件,尽管它提供了一个小型框架来更快地开发它们,但它需要了解的关于囚犯镜像的唯一信息是其基地址。Jailhouse 期望囚犯入口点位于 0xffff0(这与 x86 复位向量不同)。apic-demo.bin 是 Jailhouse 附带的标准演示囚犯,并且囚犯的框架链接器脚本确保如果二进制文件映射到 0xf0000,则入口点将位于正确的地址。apic-demo 只是一个名称;它可以是您想要的几乎任何名称。
最后,使用以下命令启动单元:
sudo tools/jailhouse cell start apic-demo
现在,切换回您运行 QEMU 的终端。您将看到像这样的行被发送到串行端口
Calibrated APIC frequency: 1000008 kHz
Timer fired, jitter: 38400 ns, min: 38400 ns, max: 38400 ns
...
apic-demo 纯粹是一个演示囚犯。它对 APIC 定时器(在每个现代 CPU 核心上都找到)进行编程,使其以 10Hz 的频率触发,并测量事件发生之间的实际时间。抖动是预期时间和实际时间之间的差异(延迟),抖动越小,虚拟机监控器的可见性(就性能而言)就越低。虽然此测试不是很全面,但它很重要,因为 Jailhouse 的目标是实时囚犯,并且需要尽可能轻量级。
Jailhouse 还提供了一些获取单元统计信息的方法。在最基本的层面上,/sys/devices/jailhouse 下有 sysfs 接口。存在一些工具可以很好地打印此数据。例如,您可以使用以下命令列出系统上当前存在的单元:
sudo tools/jailhouse cell list
结果如图 3 所示。“IMB-A180”是根单元的名称。还列出了其他单元,以及它们的当前状态和分配的 CPU。“Failed CPUs”列包含触发了一些致命错误(如访问不可用的端口或未分配的内存区域)并被停止的 CPU 核心。

图 3. Jailhouse 单元列表——相同的信息可通过 sysfs 接口获得。
要获得更详细的统计信息,请运行
sudo tools/jailhouse cell stat apic-demo
您将看到类似于图 4 的内容。数据会定期更新(与 top
实用程序一样),并包含各种底层计数器,如发出的超调用数或模拟的 I/O 端口访问数。每个条目都给出了生命周期总值和每秒值。它主要供开发人员使用,但更高的数字意味着囚犯更频繁地导致虚拟机监控器的参与,从而降低性能。理想情况下,这些值应该接近于零,就像 apic-demo 中的抖动一样。要退出该工具,请按 Q。

图 4. Jailhouse 单元统计信息让您深入了解单元如何与虚拟机监控器通信。
拆除它Jailhouse 附带了几个演示囚犯,不仅仅是 apic-demo。让我们尝试一些不同的东西。使用以下命令停止囚犯:
sudo tools/jailhouse cell destroy apic-demo
JAILHOUSE_CELL_DESTROY: Operation not permitted
这是什么原因呢?记住 apic-demo 单元在单元列表中具有“running/locked”状态。Jailhouse 引入了锁定状态以防止更改配置。锁定虚拟机监控器的单元本质上比根单元更重要(可以将其视为在发电厂执行一些关键工作,而 Linux 主要用于该系统上的管理目的)。幸运的是,apic-demo 是一个玩具囚犯,它会在第一次关闭尝试后解锁 Jailhouse,因此第二次尝试应该会成功。再次执行上述命令,apic-demo 应该会从单元列表中消失。
现在,创建 tiny-demo 单元(最初用于 tiny-demo.bin,也来自 Jailhouse 演示囚犯集),并以通常的方式将 32-bit-demo.bin 加载到其中
sudo tools/jailhouse cell create configs/tiny-demo.cell
sudo tools/jailhouse cell load tiny-demo
↪inmates/demos/x86/32-bit-demo.bin -a 0xf0000
sudo tools/jailhouse cell start tiny-demo
查看主机中的 com2.txt(与您启动 QEMU 的目录相同)。这不仅表明单元可以被囚犯重用,前提是它们具有兼容的资源要求,而且还证明 Jailhouse 可以运行 32 位囚犯(虚拟机监控器本身和根单元始终以 64 位模式运行)。
当您完成 Jailhouse 时,您可以使用以下命令禁用它:
sudo tools/jailhouse disable
要使此操作成功,必须没有处于“running/locked”状态的单元。
这是我们 Jailhouse 短途旅行的结束。希望您住得愉快。目前,Jailhouse 还不是一个随时可用的产品,因此您可能看不到它的直接用途。但是,它正在积极开发中,并且在 Linux 生态系统中有点独特,如果您需要实时应用程序虚拟化,那么密切关注其进展是有意义的。
真实环境中的 JailhouseQEMU 非常适合试用 Jailhouse,但也可以在真实硬件上对其进行测试。但是,您永远不应该在您的 PC 上执行此操作。使用像 Jailhouse 这样的底层工具,您很容易挂起运行 Linux 的根单元,这可能会导致文件系统和数据损坏。
Jailhouse 附带了一个辅助工具来生成单元配置,但通常您仍然需要调整生成的文件。该工具依赖于 Python;如果您的测试板上没有 Python,Jailhouse 允许您收集所需数据并在您的主 Linux PC 上生成配置(这是安全的)
sudo tools/jailhouse config collect data.tar
# Copy data.tar to your PC or notebook and untar
tools/jailhouse config create -r path/to/untarred/data
↪configs/myboard.c
配置工具读取 /proc 和 /sys 下的许多文件(无论是收集的还是直接读取的),分析它们并生成内存区域、PCI 设备列表以及 Jailhouse 运行所需的其他内容。
后处理生成的配置主要是一个反复试验的过程。您启用 Jailhouse 并尝试做一些事情。如果系统锁定,您分析串行输出并决定是否需要授予访问权限。如果您尝试在内存受限的系统(少于 1GB RAM)上运行 Jailhouse,请小心虚拟机监控器内存区域,因为配置工具目前可能会出错。不要忘记像在 QEMU 中一样,通过内核命令行为 Jailhouse 保留内存。在某些基于 AMD 的系统上,您可能需要调整内存映射 I/O (MMIO) 区域,因为 Jailhouse 尚不支持 AMD IOMMU 技术,尽管配置工具暗示它支持。
要捕获 Jailhouse 串行输出,您可能需要一个串行转 USB 适配器和 null modem 电缆。许多现代主板没有 COM 端口,但它们有您可以连接插座的接头(布线如图 a 所示)。将您的板连接到主 Linux PC 后,运行 minicom 或类似的程序以查看输出(记住在程序的设置中将端口的波特率设置为 115200)。

图 a. 在裸机上运行 Jailhouse 的必备工具包:串行转 USB 转换器、null modem 电缆(已连接)和可安装的 COM 端口。(图片来自 Yulia Sinitsyna。)
资源静态系统分区和 KVM(KVM 论坛 2013 幻灯片): https://docs.google.com/file/d/0B6HTUUWSPdd-Zl93MVhlMnRJRjg
kvm-kmod: http://git.kiszka.org/?p=kvm-kmod.git
Jailhouse AMD64 端口: https://github.com/vsinitsyn/jailhouse/tree/amd-v
Jailhouse ARM 端口: https://github.com/siemens/jailhouse/tree/wip/arm