---
menu: 虚拟化兼容性
title: 常见虚拟化/仿真技术的软件兼容性
taxonomy:
    category: docs
---

“容器编排”无疑是过去十年软件部署领域最火的技术方向，但为了搞懂它到底是什么，我们需要先了解一下软件兼容性和常见的虚拟化/仿真技术。

### 软件的三层兼容性（可移植性）

> 可移植性：一个可以在某台机器上运行的软件，复制到另一台机器上之后，是否还能运行。

在软件开发中，无论是使用二进制分发（例如 Windows 版微信）还是源代码分发（例如 PHP 网站），都存在三个层次的兼容性（可移植性）。

1. **指令集兼容性**（ISA Compatibility）：主要针对二进制软件，指的是针对 x86 编译出的二进制软件无法运行在 ARM 平台上。
2. **二进制库兼容性**（Application Binary Interface Compatibility）：指的是 Windows 版软件依赖的`.Net Framework`在 Linux 系统上没有，所以 Windows 版微信无法直接在 Linux 上运行。此外，在移动开发领域，不同版本的 Linux 发行版之间的 ABI 兼容性并不好，例如 Apache 这个软件在红帽系发行版上叫`httpd`，而在 Ubuntu 系发行版上叫`apache2`。
3. **环境兼容**（Environmental Compatibility）：包括配置文件、环境变量、数据库配置、文件系统、用户权限等。在实际运维过程中，大部分长期运营的系统所遇到的兼容性问题都是环境兼容问题，即使是看似最简单的兼容性问题也会让运维人员精疲力尽。

说完了软件兼容性，下面我们还需要结合这三层兼容性了解一下常见的虚拟化/仿真技术，以及它们能解决哪些层面的兼容性。

### 常见的虚拟化/仿真技术

学习过计算机科学的基础课程《计算机组成原理》的小伙伴都知道，计算机从硬件到操作系统的设计其实到处都充满了“模拟”的设计方法，从最底层的使用晶体管模拟逻辑门电路，到最上层的使用 CPU 和操作系统配合完成的“硬件中断”来模拟“事件驱动”功能，计算机的出现，就是一层又一层虚拟化和仿真思想的应用结果。

针对和容器相关的虚拟化，笔者总结出了如下几种虚拟化/仿真的方法。

#### 硬件虚拟化

大家日常使用的云计算平台提供的云主机就属于硬件虚拟化的范畴，对应的在物理机上直接部署的虚拟化宿主操作系统有 VMware 的 ESXi 和微软的 Hyper-V。硬件虚拟化是最常见的虚拟化，你在你的电脑上安装的“虚拟机”软件，在 Windows 或者 macOS 上创建 Linux 虚拟机，就属于硬件虚拟化的范畴。

硬件虚拟化可以给每个虚拟机提供虚拟出来的 CPU、内存、磁盘、网卡甚至是显卡。跟大家想象的可能不太一样，今天的硬件虚拟化其实绝大部分已经由硬件直接提供了，换句话说，今天的 CPU、主板芯片组甚至是硬盘控制器，已经全部内置了用于支撑高性能低损耗虚拟化的集成电路面积，虚拟机早就不是纯软件虚拟机了。Intel 的 VT-d 技术和 AMD 的 AMD-V 技术就是 CPU、内存等进程相关的硬件虚拟化技术。

此外，SR-IOV（Single Root I/O Virtualization）作为一种针对 PCIe 设备的硬件虚拟化技术，正在大规模普及。其核心优势在于能够突破宿主机操作系统的软件性能瓶颈（例如在[《性能之殇（四）-- Unix 进程模型的局限》](https://lvwenhan.com/tech-epic/495.html)一文中所述的上下文切换性能极限）。通过让虚拟机直接在硬件虚拟化层面调用底层硬件，SR-IOV 能够显著提升虚拟机的 I/O 速度，即网络传输速度和磁盘读写速度。

容器和容器编排技术作为一种操作系统内的资源管理方法，理论上和硬件虚拟化没有交集。不过，由于今天的服务器 CPU 的核心数实在是太多了，如今绝大多数的容器其实并不是运行在裸金属服务器上的，而是运行在经过了硬件虚拟化的虚拟机中。

#### 指令集仿真

指令集仿真技术通过软件将虚拟机指令集转换为宿主机指令集，提供了无比强大的兼容性，甚至可以在浏览器中运行 Windows 98，如图 2-5 所示。大名鼎鼎的 QEMU 即属于指令集仿真。

![](/media/16889553324232.jpg)
<center>图 2-5 在浏览器中运行 Windows 98</center>

无论是硬件虚拟化，还是指令集仿真，其运行的虚拟机都是一个完整的操作系统，拥有完全独立的系统文件、内存空间、基础库和运行环境，可以实现整个操作系统的整体迁移，理论上讲甚至可以从 x86 宿主机迁移到 ARM 宿主机。

容器和容器编排技术并未用到指令集仿真，它们是一种更高级的资源管理方法，适用于在机器数量很多的环境中进行操作系统内部资源的统一平台化管理。

#### 运行库虚拟化

折腾过 Linux 当做软件开发桌面操作系统的读者一定都用过 Wine，它可以让你在 Linux 下运行 Windows 微信，这就是一种运行库虚拟化技术，它的本质是用软件的形式模拟 Windows 操作系统以及它上面的 .Net Framework 之类的二进制库，这个技术看起来非常的具有“邪典”气质，当然，其兼容性也是最差的，往往 QQ 发了一个新版本，Wine 就得赶紧更新，不然就会各种闪退好不热闹。

对了，微软在 Windows 10 中引入的 Windows Subsystem for Linux（WSL）也是一种运行库虚拟化技术，比虚拟机技术更快，资源浪费更少，有微软的工程师提供支持，兼容性也更好，而且 Linux 自身的 ABI 稳定性一直不错。当下，从后端开发到 AI 社区，WSL 出镜率都很高，微软这一步棋目前看来是走对了。

容器和容器编排就运行在这一层，不过和前面跨操作系统的情况有一个不小的区别：与跨操作系统的情况不同，容器包含的是应用程序及其依赖项的完整运行时环境。容器将应用程序与其所在主机系统隔离开来，提供了一种轻量级的、可移植的方式来部署和管理应用程序。容器编排则负责管理和协调多个容器的运行，以实现自动化部署、扩展和管理等功能。

在容器自己看来，自己就是运行在裸操作系统上的一个普通的进程，可以获取到完全真实的硬件情况。只是它对于系统库的调用会被容器运行时给劫持，容器之间无法相互感受到对方的存在。

#### 虚拟机编程语言

一说到虚拟机编程语言，Java 一定是大家第一个想到的语言。早年间，Java 整天号称自己“一次编译，到处运行”，说自己可以统一从服务器到桌面再到嵌入式设备的全部软件运行场景，实际结果大家也看到了：也许 Java 可以，但它在大多数平台上都不是最优解，最终导致它还是只能运行在最重的服务器和大型桌面软件的背后。

Java 的 JVM 提供了一套标准：将 Java 代码编译成字节码，由字节码解释器对其进行解释运行。这些字节码再调用 JVM 专为各种平台各种架构所开发的高度兼容的 API，实现了相当高的“可移植性”，至少在技术上，它做到了。但是，这种兼容性并不属于前面三层的任意一层，因为 Java 技术本身要求你用 Java 语言来编写软件，在逻辑上，这并不是兼容，而是一种 DSL（领域特定语言），只是这种 DSL 能够跨平台运行罢了。

除了 JVM 字节码虚拟机，.Net 的 CLR 也是一种类似的技术，不过 CLR 运行的是二进制程序（Native Code）。

#### 文本解释型编程语言

作为一名 PHP 开发者，笔者觉得很有必要向大家展示一下文本解释型编程语言的技术优势，任何一个流行技术，肯定是解决了某一方面甚至是某几方面的大痛点，才得以流行起来的。

笔者知道很多 Java 开发者对 PHP 不屑一顾，但笔者认为 Java 程序员尤其需要“开眼看世界”。笔者不止一次地遇到拿 Java 概念来类比操作系统底层逻辑的程序员了，如果只懂 Java 和 JVM，虽然你也能写出能用的软件，也能赚到钱，但是计算机运行的不是 JVM 操作系统，Java 技术不是计算机的一切。作为软件开发者，我们还是要不断地“向更底层看去”，不断加深对计算机的理解，成为更优秀的自己。

文本解释型编程语言，最典型的就是 PHP，其本质也是一种可以跨平台运行的软件技术，它采用文本 + 解释器的方式运行，利用专门针对多个操作系统开发的二进制解释器来边解释边运行。PHP 代码文本可以看做一种更加粗糙的字节码，由于 PHP 一直被用在小规模 Web 系统中，所以长期以来，性能低下、API 混乱、代码质量不高就成了 PHP 的知名缺点。但是 PHP 最大的优点其实是他的运行方式：

1. 每个 HTTP 请求都会开启一个 PHP 进程，页面输出完进程就退出，这就在很大程度上让 PHP 完全不用管内存泄露问题，即便质量低劣的应用代码也可以以一种“看起来 OK”的方式长期稳定地运行。
2. 动态载入文本文件使得 PHP 成为了一种“半微服务”架构，如果某个菜鸟提交了一个有语法错误的文件，现有的页面如果对它没有依赖，那现有页面就不会崩溃：每个 URL 都可以看做一个独立的微服务。

相对的，Java 技术虽然常驻内存，自己在内存里就可以解决很多 PHP 必须依赖 Redis 和 MySQL 才能实现的需求，性能更强，但是它更容易因为一段代码的 Bug 就整体崩溃，更容易出现内存溢出（OOM）问题，运维和 Debug 的难度更大。
