一条 iPhone 线程
0:00一台电脑正在一部 iPhone 里面启动。
屏幕上有窗口,有键盘,也有一套完整操作系统。
这款应用叫 UTM。它是一款运行在 iPhone 和 Mac 上的虚拟机应用;在眼前这个场景里,真正负责模拟处理器、内存和设备,拼出另一台电脑的底层程序,叫 QEMU。
但在应用内部,负责运行这台电脑的程序并不是一个可以随时拉起的普通子进程。
iOS 不允许它这样做。
于是 UTM 把 QEMU 链接成一座共享程序库,再把 QEMU 的主循环放进一条 POSIX 线程。屏幕、键盘和应用界面留在 iOS 一侧;那台看不见的机器,则在线程里继续转动。
[001]
引用 [001]
UTM 固定 commit 的项目架构文档支撑 iOS 嵌入方式;只证明 UTM 的 QEMU backend,不代表其 Apple Virtualization backend,也不代表 Apple 自家 Simulator。
Architecture at commit d951596a, Introduction and iOS backend sections: QEMU backbone, shared library, pthread main loop, and platform UI
UTM 还维护着自己的 QEMU 分支。应用需要暂停虚拟机、保存快照、切换光盘时,它不必伸手改动线程里的每一个内部状态,而是通过 QMP,QEMU 的机器协议,把命令送进去。
在 macOS 上,UTM 不必把 QEMU 留在应用的一条线程里。系统允许启动新进程,但应用沙箱又要求权限隔离。UTM 于是通过 XPC 启动一个拥有独立沙箱的辅助进程,再由它拉起 QEMU。
于是,同一个 QEMU,在 iPhone 上藏进应用的一条线程;在 Mac 上,则隔着沙箱成为独立进程。UTM 没有换掉底层机器,只是为两个系统重新安排了它在哪里运行、怎样被控制。
这件事反常识的地方,不是一部手机居然能运行另一套系统。更奇怪的是:一个项目已经被拆开、包裹、遥控,还维护着下游分支,为什么它仍然可以被认作 QEMU?
[002]
引用 [002]
文档支撑 custom fork、QMP 控制和 macOS XPC 进程边界;不证明 UTM 所有功能都由 QEMU 提供。
Architecture at commit d951596a, QEMUKit/QMP and macOS XPC sections
答案要从一台更小的机器开始。
时间只倒退这一次。
回到二零零三年。此后,我们沿着它向前走。
从程序到机器
2:27三月二十三日,一封后来被 QEMU 维护者保存进演讲材料的首发邮件里,Fabrice Bellard 公布了 QEMU 零点一版。
Bellard 是一名法国程序员。此刻,他给这个新项目安排的第一项工作并不是模拟一台完整 PC,而是在 x86 或 PowerPC Linux 主机上运行 x86 Linux 程序。他特别提到了 Wine:如果处理器不同,原本为 x86 准备的 Windows 程序也许仍能借这条路径运行。
这更像一个程序翻译器。它接住一批 guest 指令,把它们变成 host 能执行的代码,再缓存已经翻译过的片段。它需要理解处理器,却还不必把显示器、键盘、网卡和启动过程全部造出来。
[003]
引用 [003]
维护者回顾材料与早期 changelog 共同支撑首版目标和 user-mode 起点;原始首发邮件归档尚未定位,不能把演讲转录当作原始邮件快照。
QEMU linux-user slides p.4, transcription of the 2003-03-23 version 0.1 announcement; v0.1.5 Changelog, version 0.1 entry
这里先停一下。
最直觉的模拟方式,是读一条 guest 指令,判断它的含义,再用 host 完成同样动作。然后读下一条。这样容易控制,却要反复支付“取指、识别、执行”的成本。
QEMU 的动态翻译换了一种节奏。它先把一段 guest 代码切成基本块,再生成 host 可以执行的代码。下一次程序回到同一段,不必从头解释。Bellard 为这套过程设计了 dyngen:一部分工作在编译 QEMU 时预先完成,运行时再把短小的 host code fragments 拼成目标代码。
系统模拟又增加了另一层困难。guest 以为自己在访问物理内存,host 却必须把这些地址映射到安全的空间,还要让页表、异常和设备访问表现得像另一台机器。QEMU 的 software MMU 为这种错位建立了缓存与转换路径。
这不是后来 KVM 硬件加速的简化版本。它解决的是更一般的问题:当 guest 和 host 不是同一种处理器时,机器仍然能够跨过架构边界。
[027]
引用 [027]
Bellard 论文支撑动态翻译与 software MMU 机制;段落为口播化简化,不覆盖全部异常、self-modifying code、target 与 host 差异。
Bellard, QEMU, a Fast and Portable Dynamic Translator, pp.1-4: dynamic translation, basic blocks, dyngen, translation cache, and software MMU
Bellard 面前并不是一片空地。
Bochs 已经能够逐条解释 x86 指令,在不同主机上拼出一台 PC,代价是速度。VMware 选择在相同架构上尽量直接执行,再处理那些不能直接交给处理器的部分。Connectix 的 Virtual PC 已经是一条商业产品线,就在 QEMU 首发前一个月,它的虚拟机技术被 Microsoft 收购。
这些路线分别在可移植性、速度和产品完整性上做出了选择。QEMU 此刻没有证明自己会取代谁。它只是带来另一种组合:动态翻译、可移植的代码生成,以及一个很快开始膨胀的边界。
[004]
引用 [004]
三项来源支撑同期相邻路线与商业背景;它们不能证明 QEMU 当时逐项对标,也不能组成统一性能排名。
- Bochs, Introduction to Bochs
- Mendel Rosenblum, VMware's Virtual Platform
- Microsoft 收购 Connectix VM 技术公告
Bochs Introduction, emulation method and performance discussion; VMware Virtual Platform 1999 slides pp.5-20; Microsoft acquisition announcement dated 2003-02-19
三个月后,边界越过了一个关键位置。
零点四版已经可以启动一套几乎未修改的 Linux 内核。host 不需要打内核补丁,也不需要特殊权限。QEMU 模拟了串口控制台和一块 NE2000 网卡,甚至已经启动过 xterm。
“几乎”不能省略:guest 内核仍需修改两个字节。但 Bellard 已经把内核测试、调试和虚拟主机列为用途。
一段被翻译的程序,开始拥有自己的启动过程、内存和设备。QEMU 的对象不再只是一串指令,而是一台由软件描述的机器。
[005]
引用 [005]
Bellard 同期邮件支撑 Linux 启动、host 权限边界、早期设备和用途;guest kernel 两字节修改是必要限定,不能口播成完全未修改。
announcement body lines 39-57 and footnote lines 61-63 in local snapshot
机器一旦成立,别人需要的就不再只是更多处理器指令。
零点五版加入 VGA、SDL 显示、PS/2 键鼠、BIOS loader 和更完整的软件内存管理。每多一个部件,QEMU 都更像一台可以启动操作系统的电脑;也多出一个可以被移植、替换或维护的边界。
功能正在增加,保证却没有跟着出现。
[006]
引用 [006]
同期发布邮件支撑设备与内存能力扩展;功能列表不能单独证明成熟度或后来兼容性。
QEMU 0.5.0 release announcement, full changelog list for VGA, SDL, PS/2, BIOS loader, multi-target build, and full soft MMU
没有一张通往 1.0 的日程表
6:07有人在邮件列表上追问:QEMU 有没有开发日程?哪些功能会进入一点零?能不能用捐款,让 Bellard 投入更多时间?
Bellard 的回答没有提供路线图。
他说,QEMU 没有真正的开发计划。他在空闲时间里为了乐趣开发它,也不保证它一定会到达一点零。如果自己很快转向另一个项目,那就需要一位新的维护者。
这封邮件没有把 QEMU 称为玩具。相反,虚拟主机、内核调试和跨平台移植都已经是具体用途。它暴露的是另一种不确定:用途可以先于组织出现,使用者已经来了,项目却仍然依靠一个人的兴趣和时间。
[007]
引用 [007]
Bellard 同期邮件支撑 spare time、for fun、无 schedule、无 1.0 保证与未来 maintainer 条件;没有来源支持把 toy 当作当事人用语。
schedule email lines 91-107; QEMU 0.4 announcement lines 47-55
同一封邮件里,还有一句更容易被忽略的话。
Bellard 不使用 Windows,也不使用 Mac OS X,因此无法维护或测试这些移植。但他承认它们很重要,会在补丁足够干净时尝试合入。
项目的第一次蜕变已经发生了:作者不拥有所有采用场景,也无法亲自验证每一种机器。QEMU 想继续扩大,就必须学习接收自己用不到的代码。
[008]
引用 [008]
邮件支撑 Bellard 的平台使用边界与合并态度;最后一句是节目从材料得出的协作判断,不表示当时已有正式治理制度。
schedule email lines 98-107, priorities, GUI boundary, Windows/Mac OS X ports, and clean-enough merge condition
还有一个压力来自速度。
跨架构时,翻译指令是 QEMU 的理由。同一架构时,它也可能成为绕路。Bellard 随后发布 KQEMU 加速模块,让 x86 guest 的大部分用户态代码直接在 x86 host 处理器上运行,只把必须控制的部分交回 QEMU。
在二零零五年最初的 alpha 版本里,KQEMU 仍有专有许可边界,也只支持 Linux host。可是它已经把一个问题摆到桌上:如果真正的处理器能够替 QEMU 执行 guest 指令,QEMU 还剩下什么?
[009]
引用 [009]
Bellard 同期邮件支撑 KQEMU 的直接执行思路与当时边界;不能把 2005 的许可状态延伸到后来版本,也不据此作通用性能结论。
The QEMU Accelerator Module announcement, opening description, execution boundary, Linux host limitation, and 2005 license terms
内核拿走处理器
7:52硬件很快提供了另一种答案。
处理器厂商加入了专门的虚拟化模式。一台 guest 可以在受控环境里直接运行大量指令;遇到需要 host 介入的动作,再退出到虚拟机监控器。
Avi Kivity 参与开发的 KVM 把这套能力放进 Linux。Kivity 当时是 KVM 项目的技术负责人之一,也是最早的核心开发者。二零零六年十二月,KVM 的 userspace interface 进入 Linux 内核:用户空间可以创建虚拟机、分配 guest 内存、创建虚拟 CPU,再把它们送入运行。
[010]
引用 [010]
canonical kernel commit 与 KVM 同期论文支撑内核接口、人物关系和硬件执行边界;commit 本身不证明 upstream QEMU 当时已包含 KVM。
- Linux commit 6aa8b732, [PATCH] kvm: userspace interface
- Avi Kivity, kvm: the Linux Virtual Machine Monitor
Linux commit 6aa8b732 commit message and API diff; OLS 2007 paper pp.225-228, architecture and /dev/kvm interface
但 /dev/kvm 不是一台完整电脑。
它可以让 CPU 跑起来,却不会凭空给 guest 一块硬盘、一张网卡、一台显示器和一套 BIOS。KVM 论文把工作明确分成两边:内核负责 guest mode、内存与虚拟 CPU;用户空间负责设备模型和大量 I/O。
这正是 QEMU 已经花了几年建立的部分。
KVM 不需要再造一整台机器。QEMU 也不必坚持亲自翻译每一条 CPU 指令。两个项目第一次可以沿着 CPU 与设备之间的边界,拼成同一台虚拟机。
[011]
引用 [011]
来源支撑 kernel/userspace 分层及早期 modified QEMU 关系;论文提及 QEMU,但分层本身不意味着所有 KVM userspace 永远只能使用 QEMU。
- Avi Kivity, kvm: the Linux Virtual Machine Monitor
- Linux commit 6aa8b732, [PATCH] kvm: userspace interface
OLS 2007 paper pp.226-230, userspace I/O device model and execution flow; Linux commit 6aa8b732 description of modified QEMU providing I/O and BIOS
KVM 不是唯一需要 QEMU 设备的虚拟化系统。
Xen 走的是另一条路线。它早期通过修改 guest 操作系统来换取隔离与较低开销;后来运行硬件虚拟化 guest 时,同样需要一个 device model。Xen 维护了带有自己补丁的 ioemu,并周期性追赶 QEMU 上游。
二零零八年六月,Xen 维护者 Ian Jackson 写信说明,要把这棵 ioemu 再次与 upstream 合并。这里的“再次”很重要。采用不是复制一次源码然后结束,而是下游需求和上游变化不断拉开距离,再由人把两边重新接起来。
[016]
引用 [016]
论文与同期邮件支撑 Xen 路线和长期 ioemu fork/upstream 合并;现有材料不精确证明 Xen 首次采用 QEMU 的日期。
Xen SOSP 2003 paper abstract and architecture sections; Ian Jackson qemu and ioemu mail dated 2008-06-13, baseline QEMU 0.9.0 and merge plan
这种拼接很快不再只是两个开源项目之间的实验。
开发 KVM 的团队来自 Qumranet。二零零八年九月,Red Hat 宣布以一亿零七百万美元收购这家公司,把 KVM、桌面虚拟化产品 SolidICE,以及 KVM 的核心团队带进自己的虚拟化业务。
公告没有提到 QEMU,所以不能把收购价格写成“Red Hat 买下 QEMU”。但它改变了 QEMU 周围的组织条件:需要 QEMU 设备模型的 KVM,不再只靠一个独立团队推进;一家 Linux 发行商开始承担把整套虚拟化栈做成长期产品的压力。
两个月后,KVM 支持进入 QEMU upstream。收购不是 commit 的单一原因,现有材料也没有这样说。可以确认的,只是技术分工与组织投入正在同一个时间窗口里合拢。
[028]
引用 [028]
公告和 commit 支撑收购范围与时间先后;公告未提 QEMU,不能说 Red Hat 买下 QEMU,也不能把收购断言为 upstream merge 的直接原因。
Red Hat acquisition announcement dated 2008-09-04, transaction value, KVM/SolidICE and team description; QEMU commit 7ba1e619 dated 2008-11-05
-enable-kvm
10:47两个月后进入共同上游的,究竟是怎样一套分工?先跟着一条 guest 指令走一遍。
虚拟 CPU 进入硬件提供的 guest mode。普通计算可以直接执行,QEMU 不必逐条翻译。
然后 guest 访问了一台需要模拟的设备。处理器停下这次 guest 运行,发生一次 VM exit。控制权回到 KVM 内核,再返回 QEMU 的用户空间设备模型。QEMU 判断 guest 碰到的是哪一段 I/O,为虚拟设备完成读写,必要时准备一个中断。随后,虚拟 CPU 再次进入 guest mode。
处理器、内核和用户空间反复交接。加速不是把 QEMU 整体换掉,而是从它手里拿走 CPU 执行,再把设备世界留下。
[012]
引用 [012]
来源支撑 guest mode、VM exit、内核接口与 userspace I/O 路径;段落是口播化模型,省略具体处理器和设备的差异。
- Avi Kivity, kvm: the Linux Virtual Machine Monitor
- Linux commit 6aa8b732, [PATCH] kvm: userspace interface
OLS 2007 paper pp.226-229, Figure 1 architecture and guest execution/userspace I/O discussion; Linux commit 6aa8b732 KVM_RUN and exit-reason interface
这套模型也解释了为什么“硬件直接执行”不等于没有模拟成本。
只要 guest 一直在自己的内存里计算,硬件路径就很短。一旦它频繁敲门,读取虚拟磁盘、访问网卡寄存器、触发定时器,控制权就要在 guest、内核和 QEMU 之间来回。每次退出都要判断原因,保存和恢复状态,再决定由谁完成动作。
所以 KVM 与 QEMU 的分工不是快路径和慢路径各管各的。设备怎样设计、哪些状态留在内核、哪些状态留在用户空间,会直接改变退出次数、兼容性和迁移能力。
一台机器的中心被拆开以后,接口本身开始决定性能和正确性。
[029]
引用 [029]
论文支撑 VM exit 与用户空间 I/O 带来的路径差异;段落不提供未经材料支持的固定开销数字,也不把所有设备实现概括为同一行为。
OLS 2007 paper pp.226-230, guest execution loop, exits to userspace, I/O device model, and paravirtualized I/O discussion
这套分工先在外部 KVM QEMU tree 里发展。要让它进入 QEMU 的共同上游,Anthony Liguori 提交了一组补丁。
Liguori 此时是 QEMU 的活跃维护者,负责推动 KVM 接口合流。补丁说明没有宣告完成,第一句话反而主动缩小范围:它只实现了让一个 guest 启动所需的 bare minimum,最低限度支持。
测试数字看起来很醒目:同一次启动,在邮件所列环境里,KVM 明显快于 TCG。但这只能说明那一次测试,不能变成所有机器、所有负载上的永久倍数。
更重要的句子在后面:KVM 默认关闭,用户必须明确输入 -enable-kvm。等到测试更充分以后,再考虑默认开启。
[013]
引用 [013]
commit 与评审邮件支撑 bare minimum、单次测试、默认关闭和显式选项;性能数字不外推,活跃维护角色由同期合流行为限定,不写成唯一负责人。
QEMU commit 7ba1e619 patch lines 6-26 and option diff around lines 479-498; 2008-11-04 review mail opening and quoted patch description
从补丁表面看,这次变化可以被包装成“QEMU 多了一个 accelerator”。可它碰到的并不是一个孤立插件。
CPU 状态要在两边转换。中断要从 QEMU 设备送进 KVM 的虚拟 CPU。guest 什么时候停下,QEMU 主循环怎样等待,调试器怎样看见寄存器,保存状态时谁提供哪一部分,都会穿过原有边界。
这也解释了为什么 Liguori 刻意提交最低支持。范围太大,外部 tree 会继续远离上游;范围太小,合入以后又可能制造一条只能启动、不能承担真实工作的路径。
-enable-kvm 不是一个庆祝完成的开关。它更像一扇暂时需要用户亲手打开的门:共同代码先允许新分工存在,再用测试和后续补丁决定这扇门能开多大。
[030]
引用 [030]
diff 与评审支撑补丁穿越 CPU、interrupt、main loop 和状态边界;门的比喻与范围取舍是节目分析,不冒充提交者原话。
commit 7ba1e619 full diff for CPU state, interrupt bitmap, main-loop hooks and command-line option; review mail dated 2008-11-04
合入前一天,Kivity 在评审中指出一个危险缺口。
虚拟机在线迁移时,系统需要知道哪些内存页在传输过程中又被修改。软件翻译路径可以更新这张 dirty bitmap;可是 guest 指令一旦由 KVM 直接执行,原来的记录方式就看不到全部写入。如果不修正,迁移出去的虚拟机可能得到一份彼此不一致的内存。
这不是一个只影响代码风格的意见。它说明 CPU 执行可以被替换,机器状态却不能被切成互不相干的两半。KVM 越快地运行,QEMU 与内核越需要对同一份状态达成协议。
[014]
引用 [014]
同期评审明确支撑 dirty bitmap 与 live migration 风险;它记录的是合入时缺口,不表示所有后来版本持续存在同一问题。
Avi Kivity review dated 2008-11-04, dirty bitmap paragraph: KVM accesses do not update qemu dirty bitmap and may break live migration
补丁仍在十一月五日进入 upstream QEMU。
T O D O 没有随 commit 消失。SMP、APIC、PIT,以及更多来自 kvm-userspace tree 的改动,都还在后面。
四个月后,QEMU 零点十把 KVM acceleration 写进发布亮点。可 Liguori 同时提醒,外部 KVM QEMU tree 仍然是一棵 staging tree:大量改动已经进入零点十,但不是全部。
一次合入没有消灭分叉。它只是把两边的关系改了:外部 tree 不再是另一个永久终点,而是可以持续把改动送回共同上游的工作区。
[015]
引用 [015]
三项同期材料支撑 merge、遗留工作、正式发布和 staging tree 并存;不能把 0.10.0 写成外部 KVM tree 当场终结。
- QEMU commit 7ba1e619, Add KVM support to QEMU
- Anthony Liguori, QEMU 0.10.0 KVM tree 回复
- Anthony Liguori, QEMU 0.10.0 release announcement
commit 7ba1e619 message and T O D O list; Anthony Liguori staging-tree reply dated 2009-03-04; QEMU 0.10.0 announcement dated 2009-03-05
分叉怎样回来
15:28Xen 的重新合流也没有一次完成。
二零一零年八月,一组被称作“期待已久”的 upstream Xen device-model 补丁已经可以启动 Linux 和 Windows HVM guest,却仍缺 VGA dirty bits、PCI passthrough 和 stubdomain 支持。
能启动,只是再次进入共同代码的起点。下游已经依赖的能力如果没有对应路径,采用方就不能只因为 upstream 出现了一个版本而离开自己的 fork。
[017]
引用 [017]
同期 RFC 支撑 upstream Xen device model 的可用范围与缺口;不能写成当时已经替代 qemu-xen fork。
2010-08-13 RFC cover letter, long-awaited characterization, booted guest scope, and missing VGA dirty bits, PCI passthrough, and stubdomain support
要持续接住这些差异,QEMU 还需要知道谁来评审哪一块代码。
一个月后,项目开始采用 Linux 风格的 MAINTAINERS 文件。Anthony Liguori 和 Paul Brook 被列在 General Project Administration 下面;不同设备、处理器和子系统则各自有维护人。
Bellard 曾经说,自己转向别的项目时,QEMU 会需要一位新维护者。真正出现的结构没有这么整齐。项目没有把全部钥匙交给单一继任者,而是把钥匙拆开了。
[018]
引用 [018]
来源支撑 Bellard 的条件式预告和 2010 分区维护结构;不能由名单推断全部实际权力或雇佣关系。
Bellard schedule mail lines 91-95; Linux-style MAINTAINERS discussion and patch dated 2010-09-09/10, General Project Administration and subsystem entries
这张名单也让交接变成可以精确修改的一小块文本。
二零一二年十二月,Gleb Natapov 在 KVM Overall 和 X86 条目中接替 Avi Kivity。commit 没有宣告 Kivity 离开全部 KVM 工作,也没有替 QEMU 选出一位新领袖。它只回答一个有限问题:这部分补丁现在由谁接住。
[019]
引用 [019]
commit 精确支撑 Gleb 在两个 KVM 条目接替 Avi;不表示 Avi 的全部项目活动终止,也不构成整个 QEMU 的领导权交接。
QEMU commit a2685bcc, Take over kvm maintenance, commit message and KVM Overall/X86 MAINTAINERS diff
与此同时,Xen 的 upstream 路径继续补齐。
二零一三年三月,Xen 的 libxl 补丁准备把 Linux 上的默认 device model 改成 upstream qemu-xen;NetBSD 和 stubdomain 仍保留 traditional 路径。
即使默认切换已经进入补丁,旧路径也没有被删除。共同上游可以接住更多需求,采用方仍要承认一些机器背着兼容历史。
[034]
引用 [034]
同期补丁支撑准备中的默认切换及保留例外;不能把补丁提交日写成 Xen 正式发布日,也不能写成 traditional 路径已删除。
2013-03-01 libxl default-device-model patch, Linux default and NetBSD/stubdomain exceptions
就在 Xen 默认值切换的准备期间,KVM 的另一处维护位也发生了变化。二零一三年六月,Marcelo Tosatti 不再维护 KVM,Paolo Bonzini 在 KVM Overall 条目中接手。
同一套项目里,hypervisor 支持、设备、架构和项目管理已经由不同的人承担。发起人是不是仍在中心,越来越不能靠寻找一个名字来回答。
[035]
引用 [035]
commit 支撑 Marcelo 到 Paolo 的具体维护位变化;不外推为 Paolo 当日接管整个 QEMU。
QEMU commit c6d559d9 dated 2013-06-04, commit message and KVM Overall MAINTAINERS replacement
一个月以后,Xen 四点三发布。非 stub-domain 虚拟机现在默认使用 upstream QEMU。
从二零零八年的“再次合并”,到二零一零年能启动但功能仍缺,再到二零一三年的默认路径,Xen 没有等到一份完美补丁解决所有问题。它让 upstream 在真实缺口和兼容要求之间,逐步承担更多工作。
[037]
引用 [037]
三项同期材料支撑逐步合流到 4.3 默认路径;不表示 traditional device model 在 4.3 被删除。
- Ian Jackson, qemu and ioemu
- Stefano Stabellini / Anthony Perard, Xen device model RFC
- George Dunlap, Xen 4.3 released!
2008-06-13 ioemu merge mail; 2010-08-13 upstream Xen RFC; Xen 4.3 release announcement dated 2013-07-09
KVM 和 Xen 留下了两种不同的尾巴。
KVM 的外部 QEMU tree 在 upstream 支持出现以后,继续承担 staging;Xen 的 traditional device model 在 upstream 成为默认以后,继续承担兼容。前者保存尚未合入的新变化,后者保存不能立刻丢掉的旧行为。
把所有 fork 都看成失败,会漏掉采用发生的方式。产品需求往往不会排队等上游准备好,兼容责任也不会因为默认值改变就消失。共同上游真正要处理的,不是禁止分叉,而是区分哪些差异应该回来、哪些差异暂时必须留在外面。
这要求项目拥有的,不只是代码接收能力,还有说“不够完整”、说“暂时不能默认”,以及保留兼容路径的耐心。
[031]
引用 [031]
来源支撑两类下游路径在各自合流节点继续存在;关于 fork 功能差异和治理耐心是节目综合判断,不表示所有分叉都有回流价值。
- Anthony Liguori, QEMU 0.10.0 KVM tree 回复
- Ian Jackson, libxl: change to qemu-xen (upstream) by default
- George Dunlap, Xen 4.3 released!
2009-03-04 KVM staging-tree mail; 2013-03-01 Xen default patch exceptions; Xen 4.3 release default-device-model statement
没有唯一的交接日
19:02到二零一三年,KVM 和 Xen 的合流、默认值与兼容路径,已经由不同维护位分别推进。Bellard 是否仍在日常中心,已经不再决定这些工作能不能继续。
但 Bellard 何时停止日常维护,现有材料没有给出一条干净的日期。
二零一三年七月,Liguori 在一场许可证讨论中明确写道,Bellard 已经不再参与这个项目。这句话只能夹定一个结果,不能替我们补出告别过程。
但是二零零四年的问题,到这里已经有了答案。QEMU 没有靠保证 Bellard 永远留下来走向一点零。它靠共同上游、评审边界和一张不断变化的维护名单,把“谁来继续”拆成了许多可以接手的具体问题。
[020]
引用 [020]
同期维护者邮件支撑 2013 年 Bellard 已不再参与;没有单一退出日期,不能虚构正式交接或离开动机。
Anthony Liguori license-discussion mail dated 2013-07-31; Bellard schedule mail dated 2004-08-31
同年九月,前面推动 KVM 支持进入 QEMU 的 Anthony Liguori,在 LinuxCon 回看 KVM 和 Xen 留下的 forks。他没有把这段历史讲成下游终于服从上游,而是把问题转向 upstream 自己愿意改变多少。
Anthony Liguori(原声): One of the things we learned from these forks, though, and I think Linus says similar things: once the distro ships something for three years, it doesn't really matter if you thought the code was ugly. It's obviously useful code that people care about. So some of what it took to merge those forks back was realizing that maybe we needed to compromise a bit on our quality - not on our quality, but on our standards and sort of what we'd be willing to tolerate from a design perspective.
这里改变的不是“质量可以不要”,Liguori 在原声里立刻修正了这个说法。改变的是谁有资格证明一种设计有价值:当用户已经围绕一棵 fork 生活了三年,拒绝它也不再只是代码审美。
[038]
引用 [038]
Liguori 的参与者后见总结支撑长期 downstream 使用与 upstream 调整标准之间的关系;不能外推成所有 fork 都应合入,也不能替代具体 commit 的时间边界。
LinuxCon 2013 video approximately 00:11:58.5-00:12:29.5; downstream shipping for three years, useful code, and upstream design-standard compromise
名单还在继续变化。
二零一三年十月,Paul Brook 因邮件退回和长期不活跃,被移出项目管理和多个子系统条目。名单接下来还会变化。共同上游面对的另一条压力,也在同一时间靠近:采用者怎样让自己的分支不再越走越远?
[036]
引用 [036]
commits 精确支撑 Paul、Peter、Anthony 的维护条目变化;不把有限职责变化外推为整个项目的唯一领导权交接。
- QEMU commit 0e19885e, Update MAINTAINERS
- QEMU commit ff0d4876, add Peter Maydell to general project admin
- QEMU commit 238d7497, drop Anthony Liguori
QEMU commits 0e19885e dated 2013-10-02, ff0d4876 dated 2014-10-15, and 238d7497 dated 2015-03-10; commit messages and MAINTAINERS diffs
二零一四年九月,在 Linaro Connect 一场关于 ARMv8 和六十四位 Android Emulator 的演讲里,负责 Android 部分的 Alex Bennée 谈到他们正在与 Google 推进的方向。这里说的是当时的目标,不是后来版本的完成公告。
Alex Bennée(原声): We're currently talking with Google, who seem very keen on moving forward to having the new Android Emulator based on upstream QEMU. So even if not everything that we've worked on gets upstream into QEMU itself, we're able to have a constantly updated small delta against the mainline QEMU for the Android Emulator.
这段话里真正有重量的不是 Google 这个名字,而是 small delta。downstream 没有消失,它从一棵可能不断分叉、越长越远的树,变成一段必须持续测量、持续缩短的距离。
[039]
引用 [039]
演讲支撑 2014 年正在推进的 upstream 目标与 small-delta 工程取向;不能写成当时已经全部完成,也不能外推所有后续 Android Emulator backend。Alex Bennée 的人物归属已由完整视频确认,最终版本已按技术史评论的最短必要引用边界完成发布判断。
Linaro Connect 2014 video approximately 00:07:22.0-00:07:49.0; new Android Emulator based on upstream QEMU and a continuously updated small delta
一个月后,Peter Maydell 加入 General Project Administration。二零一五年三月,Liguori 经私下确认,从项目管理和多个子系统条目中退出。
Android 的采用计划与维护名单变化不是一条可以互相解释的因果链。但它们同时暴露了同一种压力:下游在努力控制自己与 upstream 的距离,上游的职责则继续在不同维护者之间移动。
这些 commits 每一条都只说明一个有限位置发生了变化。它们不能拼成某一天“QEMU 完成交接”的戏剧场面,却能证明另一件事:项目已经可以在不同部位失去维护者、补上维护者,而不必等待发起人回来决定一切。
[036]
引用 [036]
commits 精确支撑 Paul、Peter、Anthony 的维护条目变化;不把有限职责变化外推为整个项目的唯一领导权交接。
- QEMU commit 0e19885e, Update MAINTAINERS
- QEMU commit ff0d4876, add Peter Maydell to general project admin
- QEMU commit 238d7497, drop Anthony Liguori
QEMU commits 0e19885e dated 2013-10-02, ff0d4876 dated 2014-10-15, and 238d7497 dated 2015-03-10; commit messages and MAINTAINERS diffs
四个窗口,四种 QEMU
22:50维护名单回答了 QEMU 的代码由谁继续接住。采用者还要回答另一个问题:由内核、设备模型和管理工具拼成的机器,怎样被长期支持和交付?
IBM 的 PowerKVM 技术书把栈画得很清楚:KVM 提供硬件虚拟化,QEMU 运行 host 侧的虚拟机与设备,libvirt 再从上面管理。SUSE 的产品文档也把 QEMU-KVM host、libvirt 封装和直接调用 qemu-system 的路径分别写出。
大公司采用的不是一个孤零零的可执行文件,而是一组可以分层支持的职责。出了问题,CPU 执行、设备模型、管理接口和发行版打包必须能够分别定位,也必须共同维护。
但服务器机房离大多数听众很远。真正让这种分层变得可见的,是我们每天会碰到的几个窗口。
[032]
引用 [032]
产品方文档支撑 IBM 与 SUSE 栈内的职责分层;不能外推所有 IBM/SUSE 虚拟化产品,也不用于给出行业采用比例。
IBM PowerKVM Configuration and Use pp.13-16, KVM/QEMU/libvirt stack; SUSE Managing KVM Virtualization, architecture and qemu-system/libvirt management sections
这套结构后来抵达用户桌面时,QEMU 常常已经不再长得像 QEMU。
先看 Android Emulator。前面那段二零一四年的原声,说的是尽量靠近 upstream、维持 small delta 的目标。六年后,一份固定版本的 Google 源码仓库,让我们看见这段差异具体包含什么。
仓库直接把 Android Emulator 称为 QEMU 的 downstream fork。它保留 CPU 和 system emulation,又增加 Android 手机需要的硬件、OpenGL、GPS、GSM、传感器和图形界面,也保留了从 upstream 合并的工作路径。
一台通用机器到了这里,被装进了电话的身体。设备可以特化,关系没有切断;原声里的 small delta,到这里变成了必须长期维护的工程动作。
开发者在窗口里拖动位置,给模拟设备输入一组经纬度;应用收到的却像是手机传感器发来的位置。模拟来电、网络状态和旋转屏幕也是同一种动作:桌面按钮越过 GUI 和控制层,最后变成 guest 眼中的硬件事件。
对 Android 开发者来说,QEMU 的价值不是显示一台“通用 PC”。它要足够通用,才能被改成一部并不存在于桌面上的手机;又要足够具体,让应用真的面对 GPS、GSM 和 sensors,而不是一张手机外形的截图。
[021]
引用 [021]
演讲支撑 2014 年 upstream / small-delta 目标,固定 AOSP commit 支撑 2020 快照中的 QEMU downstream、设备 / GUI 扩展和合流流程;两者不能证明全部后续 backend,也不把快照日期当作首次采用年份。
- Alex Bennée、Christoffer Dall、Peter Maydell, QEMU for ARMv8 and the 64-bit Android Emulator
- AOSP Android Emulator README at commit 2db80f7c
Linaro Connect 2014 video approximately 00:07:22.0-00:07:49.0; AOSP external/qemu README at commit 2db80f7c, Overview, Features, Android-specific additions, and upstream merge workflow
再看 UTM。
Android 改写的是设备世界,UTM 连 QEMU 怎样存在于操作系统里都改了。在 iOS 上,它是一座 shared library 和一条线程;在 macOS 上,它可以成为 XPC 管理的进程。QMP 把应用按钮重新接回机器状态。
终端用户点击暂停时,不需要知道 monitor command,也不需要看见 QEMU 的命令行。QEMU 失去了产品界面,却保留了可控制的机器边界。
[022]
引用 [022]
固定架构文档支撑 UTM 对进程形态、控制层与 UI 的改写;不证明 UTM 的每个虚拟化 backend 都使用 QEMU。
Architecture at commit d951596a, platform backend table, QEMUKit/QMP, iOS pthread, and macOS XPC sections
第三个窗口里是一台原始 Xbox。
xemu 项目说明自己延续自 XQEMU,并建立在 QEMU 这套 generic machine emulator 之上。它不是给通用 PC 虚拟机换一张皮,而是以这台通用机器为工程起点,重建一台具体游戏主机。
这里被改写的是整台机器的身份。
我们只能把这句话说到 xemu。Dolphin、RPCS3、Xenia 等模拟器没有项目方证据进入这条 QEMU 系谱。窗口里都是游戏,不代表底层属于同一个家族。
[023]
引用 [023]
项目官方说明支撑 xemu/XQEMU 与 QEMU 的关系;证据只覆盖原始 Xbox 这一系,不外推其他游戏主机模拟器。
xemu About page, project purpose, XQEMU lineage, supported desktop hosts, and built-on-QEMU statement
第四个窗口最安静。
GNOME Boxes 让 Linux 桌面用户选择系统镜像、分配资源、打开一台虚拟机。它的技术说明列出 qemu-kvm、libvirt-glib 和 spice-gtk:QEMU/KVM 运行机器,libvirt 管理它,SPICE 把图像和输入送到桌面窗口。
这里被改写的不是处理器或设备,而是用户与复杂性的距离。命令行、machine type 和设备参数退到界面之后。QEMU 被普通用户使用的标志,恰恰可能是用户不再需要知道它的名字。
[024]
引用 [024]
GNOME 官方帮助支撑当前技术栈与界面分层;不能单独证明最早采用年份或所有发行版的具体打包。
GNOME Boxes help, What is the technology used by Boxes, qemu-kvm, libvirt-glib, spice-gtk, display and input explanation
四个窗口背后,也站着四种不同的用户。
Android 开发者想复现一部手机的状态。UTM 用户想在 Mac 或 iPhone 上启动另一套操作系统。xemu 玩家想让一台已经停产的游戏主机重新出现。Boxes 用户可能只想安全地试一个 Linux 镜像。
他们不共享目标,也不需要学习同一组命令。共享的是一层更低的工程承诺:CPU、内存、设备、显示和控制可以重新组合;采用方可以拿走其中一部分,并把自己的差异留在明确边界上。
如果 QEMU 只是一款固定界面的虚拟机产品,这四种需求会互相挤压。它成为底座以后,差异反而是采用发生的入口。
[033]
引用 [033]
四个项目方来源支撑不同产品目标和技术边界;对用户目标的概括保持在官方功能范围内,不声称所有用户动机相同。
- AOSP Android Emulator README at commit 2db80f7c
- UTM, Architecture at commit d951596a
- xemu, About
- GNOME Boxes, What is the technology used by Boxes?
AOSP Android-specific feature list; UTM Architecture platform backends; xemu About purpose statement; GNOME Boxes technology and user-facing VM management description
相似的窗口也会制造错误直觉。
Apple 的 iOS Simulator 看起来同样在 Mac 上运行一部手机。但 Apple 工程师在 WWDC 明确解释,Simulator 的代码为 Mac CPU 原生构建,iOS、watchOS 和 tvOS 的 userspace 直接运行在 macOS kernel 上。
“It’s not an emulator.”
它不是 QEMU 采用案例。UTM 是第三方应用把 QEMU 带进 iOS 和 macOS;Apple Simulator 则是一条不同的运行路径。
判断一个窗口背后有没有 QEMU,不能看它画出了什么外壳,只能沿 CPU、内核、设备和控制接口逐层追下去。
[025]
引用 [025]
Apple 官方演讲用于排除自家 Simulator 基于 QEMU 的说法;不能外推第三方 UTM,也不否认 Simulator 对平台 API 和设备行为的模拟。
WWDC19 session 418 transcript, explanation that Simulator is a separate userspace running on macOS kernel, built natively for Mac CPU, followed by It's not an emulator
一台不再属于一个人的机器
28:24现在回到那条 iPhone 线程。
QEMU 被链接成共享程序库,藏在应用界面下面。它可以不拥有窗口,不拥有进程的外形。在 KVM 路径上,它甚至不必拥有 CPU 的主要执行工作。
它留下来的,是一组仍然可以组合的边界:处理器可以换成硬件加速,设备可以改成手机或游戏主机,控制可以交给 QMP,显示可以交给 SPICE,产品可以维护自己的 fork。
如果这些改动只向外走,QEMU 最终会碎成一批互不相干的项目。共同上游、协议和分区维护做的,是让改动仍有可能回来,让采用者不必在“完全服从原版”和“永远独自维护”之间二选一。
[026]
引用 [026]
多项一手材料共同支撑可替换执行、下游特化、控制接口和分区维护;关于避免孤岛的表述是节目基于这些关系作出的综合判断,不表示所有 fork 都已回流。
- UTM, Architecture at commit d951596a
- QEMU commit 7ba1e619, Add KVM support to QEMU
- AOSP Android Emulator README at commit 2db80f7c
- QEMU, The Role of Maintainers
UTM Architecture at d951596a; QEMU commit 7ba1e619; AOSP external/qemu README at 2db80f7c; QEMU current Role of Maintainers documentation
所以,QEMU 的故事不是一个玩具终于变得严肃。
二零零三年的它已经有明确用途;二零零四年的 Bellard 只是诚实地拒绝保证未来。真正的蜕变,是一台由个人写出的机器,逐渐允许别人拿走它最显眼的部分。
KVM 拿走 CPU。Android 增加手机设备。xemu 改写整台 machine。Boxes 隐去命令行。UTM 把它塞进一条线程。
它被业界接受,不是因为所有人最终运行同一个原版程序,而是因为越来越多人敢于在它上面留下自己的不同。
一部 iPhone 的屏幕上,另一台电脑继续启动。
线程里没有 Bellard,也没有一张通往终点的日程表。
机器仍然在运行。
片尾
30:01这里是《原代码》。
你可以访问《原代码》的官方网站。网站地址是,原代码三个字的全拼,点 X Y Z。在那里,你可以查看本期逐字稿、证据引用关系和证据快照,也可以收听往期节目。
感谢收听,我们下期再见。