我最近遇到了一个错误,其中一个 python 库使用了某个 CPU 指令,该指令存在于一个 x86 处理器上,但不存在于另一个处理器上,导致程序在一个系统上意外崩溃(非法指令),但在另一个系统上却没有。这让我想到了容器化为我的软件创建定义良好的运行时环境的好处。但当我意识到这是多么低级的时候,我的大脑就停止了,无论是从推理还是从网上阅读,我都无法弄清楚像 docker 这样的软件的隔离程度到底是到什么程度。
所以我的问题是:容器化软件(如 Docker 或 LXC)是否能够模拟物理硬件上不存在的指令?如果容器无法处理,完整的虚拟机是否能够处理它?
我想我会填补空白,只是因为人们很好奇。
我遇到的具体场景是当尝试将里德-所罗门擦除编码应用于数据对象时。 我正在使用 PyECLib 库,它通过
liberasurecode
库实现 Vandermonde Reed-Solomon(我相信,它又使用 jerasure)。
这段代码在兼容的处理器上运行没有错误,但在某些较旧的处理器上会产生
Illegal instruction
异常:
from pyeclib.ec_iface import ECDriver
ec_driver = ECDriver(k=1, m=5, ec_type='liberasurecode_rs_vand')
ec_driver.encode(b'foo')
我在多个 Linux 平台上使用 Python 3.6。造成严重破坏的值得注意的情况是在下面指定的处理器上运行 Fedora 25 的 LXC 容器中,但我敢打赌 LXC 和 Fedora 与它没有什么关系。
我尝试了 pyeclib 1.4 和 1.1,并且发生了同样的事情。
这些处理器使我的程序崩溃:
以下是一些运行良好的处理器:
容器不翻译指令。在容器中运行的程序与在同一台机器上运行的任何其他程序完全相同,只是它具有某些事物的单独(“命名空间”)实例,例如文件系统、网络堆栈和系统主机名。 CPU 没有被模拟或虚拟化(无论如何,比平常更多。)
虚拟机可以支持主机不支持的指令,但它们不一定这样做。如果这样做,通常会付出巨大的性能代价。
实际的答案是不,目前的产品都无法做到这一点。
想知道并不奇怪。事实上,不久前,ARM 处理器甚至在没有 VM 的情况下也能准确支持此功能,这一点很常见。在 ARM 上,浮点曾经是可选的,但在没有 FP 的处理器上,可以可靠地捕获“无效操作”异常。如果您链接了 FP 模拟库,则该库可以接管并从异常中恢复。
更现代的方法是使用备用代码路径。只需编译每个函数的两个版本即可受益于非通用指令。在运行时,检查可用的 CPU,并使用它来决定使用哪组函数。一种变体是将这些函数打包在两个 DLL/.so 中,并根据 CPU 加载两个函数之一。
Vax cpu 也做到了这一点。例如,microvax 有完整真空吸尘器的一个子集。