虚拟机十日谈:第一日 - 虚拟机总论

1,228 阅读23分钟

第一日白天: 虚拟机总论

1.1 什么是虚拟机(virtual machine)

问:什么是虚拟机呢?

答:首先你得知道什么是机器(machine),这个名词可能有多种含义,不过在我们这个系列里面,它指的是一个黑盒子。它在很多情况下指的是一台抽象的机器,有CPU、内存、外围设备和其他资源。

问:这样说,机器指的是一种硬件的集合吗?

答:取决于你怎么定义“硬件”。不过,大部分时候,机器只是一组抽象接口集合。

问:接口?

答:是的,在机器上层看来,机器不过只是接口。所谓的虚拟机,就是实现了这些接口。

问:这样看,“虚拟机”和“机器”也没有什么区别?

答:是的,“虚拟机”的“虚拟”是一个惯用词,不代表有什么特定含义,计算机领域很多词都是这样的。有很多的“虚拟机”根本就没有“真实机”,比如高级语言的虚拟机,就没有“真实机”与之对应。可以说,机器不过是虚拟机的一个实现而已。打个比方,x86ISA规定了一系列的接口,如果你采用软件来实现这些接口,称为虚拟机。如果你采用硬件来实现这些接口,习惯上你就不叫它虚拟机,而是称为机器。

1.2 模拟与仿真(emulation and simulation)

问:在虚拟机的领域,常听到两个词:“模拟”、“仿真”,它们有什么区别呢?

答:这个问题问得很有水平,因为很多从业者也没有弄清楚。我们从英文来讲,模拟的英文是emulation,它在虚拟机领域里面,指的是对于黑盒子外部接口的实现;而仿真的英文是simulation,它指的是对于一个事物内部细节的模仿。

问:你解释得更糊涂了,能举个例子吗?

答:糊涂就对了,概念就是从清晰到糊涂再到清晰的。我们举一个简单的例子,现在我们有一个黑盒子,它有两个开关和一个灯泡,红开关开灯、黑开关关灯。现在有个问题先问你,什么是它的接口?

问:... 我没有看到它有什么接口。

答:它的接口是“动作-反应”,“红开关-开灯、黑开关-关灯”。要想模拟这个黑盒子,那我们需要仿造类似的“动作-反应”。

问:那么什么又是仿真呢?

答:仿真是我们要去模拟这个黑盒子内部的构造,就是我们要仿真里面导线的连接方式,导线如何与开关、电源(它不是接口的一部分)、灯泡相连。

问:这个不就是如何实现黑盒子吗?

答:不不不,实现黑盒子有很多手段,未必要按照黑盒子的实现实现黑盒子

问:说相声呢你这?有什么具体的例子吗?

答:比如要实现1+1=2,你可以用算盘、也可以用计算器,还可以掰手指头数,这都是一种实现。如果把掰手指头作为黑盒子,那么其他都是模拟它的实现。

问:明白了。如果把算盘当成黑盒子,那么其他的方式都是算盘模拟器

答:非但如此,就连算盘本身也是一种算盘模拟器。如果你:

  • 按照黑盒子的实现实现黑盒子,并对外提供接口,那么你同时进行了仿真和模拟。你这个黑盒子的实现,就既是仿真器也是模拟器。
  • 不按照黑盒子的实现实现黑盒子,那么你就是进行了模拟,你这个黑盒子的实现,就是一个模拟器,而不是一个仿真器。
  • 按照黑盒子的实现实现黑盒子,但不对外提供接口,那么你只进行了仿真。你这个黑盒子的实现,只是仿真器。

问:不过你也看不到什么东西,只实现,而不对外提供接口吧?

答:那可不一定。非洲有个国家叫利比里亚,它仿真了美国的所有政治细节,连国旗都是高仿的。但是它国家治理一塌糊涂......

问:这么聊咱们聊天还能公开嘛?再说了,你这个例子不还是说它没有完全仿真吗?

答:实际上,完全的仿真是不存在的,你这个左派幼稚病有点大。除非你就使用原件,但是那种仿真也没有意义。我们制造仿真器,就是在某些条件不允许的情况下(比如经济条件)来复刻另一个东西。不过,你说的基本正确,所以我们谈到仿真器的时候,它都是既实现接口,又实现细节的。也就是说,一个仿真器一定是一个模拟器。

问:你这话的意思是?

答:比如说我想研究一个元器件A的细节,比如它内部的两个子器件(BC)之间如何作用。于是我用C语言实现了这个元器件,用两个类的实例来表示它的两个子器件,从而研究得以进行。

问:你这不是模拟吗?

答:从子器件的外部(也就是BC的外部)看起来,它是一种模拟,就是对于BC 的模拟;但是从A的外部看起来,它是对于A工作原理的仿真。在这个例子中,仿真建立在对于一堆器件的模拟,模拟依赖内部细节的仿真。仿真是一群器件的模拟,模拟是对于一个器件的仿真......

问:最后这句话听着很耳熟呀。说了大半天,清晰这两个概念有什么用呢?

答:用处很大:

  • 可以和别人吹水。
  • 可以清晰了解为什么有的东西叫simulator,为什么有的东西叫emulator
  • 可以想清楚虚拟机的核心概念:对于抽象接口的模拟

问:从上面第三点,好像虚拟机是一种模拟器。

答:没错。

问:听你废了半天话,一点收获也没,接着说吧,我去拿个下午茶。

1.3 解释和翻译(intepret and translate

问:我回来了,下午茶又是一堆果葡糖浆。

答:你最好按着词说。我把静安面包房送给你。

问:我在虚拟机领域里面,还听过”解释“和"翻译",它们是什么意思,和前面的模拟和仿真又有什么关系?

答:我就没见过提问这么好的学生。解释和翻译用于转换guest ISAhost ISA上。解释指的是逐句执行guest ISA,翻译指的是按照一个语义块(比如基本块basic block)来转变为host ISA的代码。

问:我懂了。解释就是逐句执行,翻译就是JIT

答:此时间不可闹笑话,胡言乱语怎瞒咱。Just-In-Time只是一种技术,它代表的只是运行时生成host ISA代码的技术。它可以用在解释上,也可以用在翻译上。而解释的实现可以用JIT,就像Hotspot的模板解释器,也可以是逐句解释。翻译的实现可以是JIT,也可以是AOT,比如C语言的翻译就是直接成host ISA

问:那么解释型语言又是什么意思呢?JavaScript是解释型语言,是不是说它只能解释?

答:少看点乱七八糟的网文。语言分类上没有解释型语言一说。之所以说JavaScript是解释型语言,可能因为它更多被人了解的是它早期的解释器。实际上就是C这种标准的AOT语言,你也可以搞一个解释器,这不代表C就是什么解释型语言。语言的分类还是要从”强类型“、”弱类型“上来说。

问:我怎么感觉和其他人说的不一样...

答:实际上就是个习惯用法而已。Java最早的时候,Sun给它配的也是被称为Sun Classic VM的虚拟机,这个虚拟机的指令处理部分也是解释执行的,当时也有人说Java是解释型语言。不过很快Java就有了翻译的虚拟机,而且你现在已经见不到什么解释型的Java虚拟机。所以Java就把解释型语言这个帽子摘了下来...

问:戴到了JavaScript头上?

答:我只想强调的,是概念实际上可以被遗忘的。JavaScript是解释型语言这个说法之所以流传很广,是因为高性能的JavaScript虚拟机出现的晚而已。

问:扯远了。那解释和翻译和前面的仿真和模拟又有什么关系呢?是不是仿真只能用解释实现,而模拟只能用翻译实现呢?

答:并没有这种关系。不过,习惯上仿真器是用来分析内部关系的,使用解释技术来实现是可以更方便调试。模拟器更多时候为了性能选择翻译实现。

1.4 虚拟机简史

问:你能简单介绍一下虚拟机的历史吗?

答:要是追溯起来,虚拟机的概念可以到图灵的”机器“......

问:图灵?

答:图灵被称为计算机之父,他做了很多基础性的思考和工作,可惜的是他最后吃了一个毒苹果......

问:后面的故事我知道,来了个王子把他吻醒了。

答:整个字跳就数你最聪明。图灵设想的”机器“实际上就是一种抽象接口,实际上就是虚拟机最早的一种概念。

问:我百度了一下,”图灵奖“是计算机科学最高奖项。

答:对的。计算机科学是最讲究实际的,所以图灵奖是一个银碗,万一计算机干不下去,还可以去要饭。

问:自打东周列国就有我们这一行。

答:现代意义的”虚拟机“是为了造CPU的仿真器而出现的。在Mimic的论文里面,提到了第一代和第二代simulator的说法,第一代的仿真器比如Z80MUCOM都是诸指令解释的一个simulator,第二代的仿真器运用了翻译手段,这种情况更像现代的虚拟机。不过要从第三代的模拟器开始,才走向二进制翻译的正轨。

问:我有两个问题:

  • 第一个,前两代都是仿真器,第三代怎么叫模拟器了?
  • 第二个,不是说虚拟机吗,怎么扯到了二进制翻译了?

答:第一个问题,那时候因为接口不清晰,所以仿真、模拟概念也不清晰。不过自从到了第三代后,我们真的可以称呼为模拟器了。因为它们的设计目的不再是重现CPU内部细节,而是用来在上工作。主要原因是,CPU细节太多了,仿真也仿不了。

第二个问题,二进制翻译是现代用来实现CPU 模拟的主要手段。

问:接着说吧。

答:整个20世纪90年代,是这类模拟器井喷的时候,也是二进制翻译技术井喷的时候。原因是在20世纪90年代,RISC技术井喷了。大家纷纷追求”制造自己的CPU“,但是发现遗产代码(原来跑在x86上的代码)和生态才是大问题。没有应用,你那些“自主研发”的CPU也没啥用。

问:你最好是在说90年代。

答:过了90年代后,尤其是英特尔公司挺过去后,大家终于意识到其实何种指令集根本也不影响处理器的效率,只是一个接口而已。

问:你这话说的有问题啊,不是活下来Arm这种公司了吗?

答:不不不,Arm公司成立的早过这波浪潮,而且Arm的指令集也不是RISC

问:越说越离谱,Arm公司可是把RISC放到自己名字里面,全称是Advanced RISC Machine。这种公司的指令集会不是RISC的?

答:你还年轻,很多时候扯了什么旗、喊了什么口号并不重要,重要的是它做了什么。ARM里面的RISC字样是当时全民围剿Intel的形势下高举的旗帜,它算不上完全的精简指令集,还是保留了传统的残留。比如说直到今天它还保留着PSTATE这种东西,都是CISC的残留,真的RISC怎么会依赖PSTATE(类似x86里面的Eflags)?它甚至不如MIPSRISC

问:但是它成功了。

答:就那样吧,不以成败论英雄。总之,RISCCISC之争,起自学术争论,很快被不满Intel垄断地位的其他公司利用(比方说Arm最早就是苹果出资的),成为一场经济之争。搞笑的是,在这场争端的快结束的时候,Intel收购了DECStrongARM改成XScale,也投身ARM和所谓的RISC阵营。

问:这个争论的后果呢?

答:其实在争论最开始,就有人认清楚了精简指令集和复杂指令集,不过是接口和借口而已,不过这种声音淹没在浩浩荡荡的“打倒CISC霸权”的口号中。人们总是喜欢反抗一个强权并且觉得自己多么伟大,所谓的CISC有问题,无非是批薄射刘,矛头直指Intel。在Intel侧,凭借遗产代码和生态挺住了第一波,又不断挖人和学习,并且靠着法务的实力吃掉了RISC派系的最伟大的成果VLIW,彻底站稳了脚跟。到了今天,Intel处理器外表是CISC,里面却是RISC的,基本上是一个二进制翻译器。通过这个争论,确确实实让处理器制造技术上了台阶。

问:看起来争论还是有意义的。

答:对,从大乱才能大治,乱了敌人,锻炼了群众嘛。哪天有机会我们细说CPU的那些事,和虚拟机一样精彩。说回到那个年代,由于其他新型的指令集,比如DECAlpha,为了要兼容其他处理器如VAXDEC以前的处理器)、MIPSSPARCx86,开发了一系列二进制翻译器,集大成的就是FX!32

问:可真够忙乎的,最终结果呢?

答:FX!32是一个基于度量的离线优化翻译器。它这种PGO的思想还是很不错的,也给后面的一些虚拟机设计以启发。

问:我的意思是DEC忙活半天,最后呢?

答:由于坚持自主研发Alpha这种无聊的处理器指令集,DEC公司过不下去了,最后卖身康柏。康柏试图卖Alpha指令集的电脑,不出意料地赔了个底朝天,最终被惠普收购。至于Alpha指令集,被江南所拿过去造了申威,最后用在超算太湖之光上。

问:为什么AlphaDEC、康柏、惠普手里都不行,在我国却取得这么高的成就呢?

答:旧社会把人变成鬼、新社会把鬼变成人嘛。

问:我还是弄不明白,在申威1600的时候,RISC技术已经被证明只是一场技术空谈,而且Alpha处理器其实也没有大卖,也没什么遗产代码在上面,为什么江南所要选这样冷门的指令集呢?

答:有人恶毒攻击是江南所通过手段拿到了DEC Alpha的设计,所以用Alpha顺利成章,当然我们要不信谣不传谣,自觉同这种谣言作斗争。

问:无中生有的东西你再帮他说一遍,等于你也有责任吧。

答:其实只需要关注一点,现代处理器里面,用什么指令集影响不了什么,但是用什么指令集影响的是处理器推广程度、因为有遗产代码存在。

问:为什么不直接采用x86指令集呢?

答:因为有诉讼大棒啊。不过北大众志就是x86的指令集。不过,的确有公司合法实现了x86

问:这是什么?

答:在RISCCISC争论的末期,RISC已经黔驴技穷了。不过这时候有一家伟大的公司Transmeta(全美达),发现了RISC最大杀手锏:VLIW,并且用了二进制翻译技术将它发扬光大,真真正正地威胁了Intel

问:听都没听过这小破公司,看起来像个送披萨的。

答:这个公司是第一个给Linus交五险一金的公司。Linus因为它才到了美国。另外,游戏界的大拿Dave Talyor也在几乎同时在Transmeta

问:那什么是VLIW,又怎么和二进制翻译联系在一起的呢?

答:VLIW全称Very Long Instruction Word(超长指令字),它通过一个奇特的角度来ILP,它把指令设计为一组子指令的集合,类似super instruction,它包含了一组操作,这些操作一起解码,一起执行。

问:等等,如果不检查子指令间的依赖就执行的话,那应该会有问题啊。

答:没错。不过,VLIW把这一切东西都委托给了编译器来处理,由编译器来检查这些,从而生成符合要求的代码,这样就保证子指令间没有问题。这样的话,实际上是显式并行了。在指令派发时候没有依赖性检查,这样处理器设计就更简单。

问:它为什么叫超长指令字呢?

答:在这种指令集上,”指令“实际上是一组小指令的集合,所以就显得比较长(128位或者更多),所以叫超长指令字。

问:我好像明白了,在指令流水里面,这一组子指令的FD是一起的,但是由于它们之间没有依赖和冲突,所以E是一起的,等于瞬间加速数倍啊。

答:没错。而且功耗降低了。

问:听起来好像和SIMD很像?

答:不不不,他是谁我是谁,别把张飞当李逵。SIMD是批量处理多组数据,但是它们的操作是一个,比如同时进行一个数组的加法。但是VLIW不一样,它可能同时进行了加法、减法等等。

问:听起来很好的技术,现代处理器应该都使用了吧?

答:并没有。使用这个技术的两个处理器,就来自我们谈的两个主角公司全美达和英特尔。全美达制造了一批处理器,它是VLIW,同时配套有软件(但是这个东西实际上算是协同设计的软件,是工作在OSBIOS以下的。这是一个二进制翻译器,所有二进制翻译人都应该记住它的名字:CMS),用来将x86指令集翻译到VLIW上。由于我们前面的介绍,这种方式是低功耗的,这也是全美达第一款处理器Crusoe系列的主要卖点。另一个卖点是无痛兼容x86,直接移植生态。实际上,Crusoe的功耗大概是P42%。(当然速度也不如Intel P4

问:这样不会遭遇专利权大棒嘛?

答:显然不会。因为翻译另一个指令集的指令并不犯法,指令集是一种语言,本身没有任何专利权可言,有的只是版权,而版权是不能阻碍其他人的使用的。

问:你这儿新东西贼多。那为什么还需要指令集授权?

答:指令集是版权,版权保护的是表达方式,不是实现方法。你只需要把指令集里面的MOVtransmeta_mov就可以避开版权了。 所谓的授权,授的是版权,不是专利权。指令集本身没有专利,但是如何实现指令集是可以有专利的。举个不恰当的比方,一个指令集就像一道菜,这道菜你可以仿制,但是名字已经被注册了(类似指令集的各个指令名),你可以换个名字卖这个菜。但是原来发明这道菜的,已经把如何做这个菜的方法注册成了专利,你需要研究怎么避开这些专利还能做出来这道菜,有时候非常难。

问:听着都新鲜。那全美达有没有侵犯专利呢?

答:显然没有,因为英特尔公司根本没有用二进制翻译实现x86指令集。

问:那英特尔为什么还要拿专利权官司起诉全美达?

答:这就是故事最精彩的地方。英特尔明明知道不能赢,但是它们聪明的法务还是要告,准备了大概30项的诉讼来要告全美达。这种诉讼威胁已经把全美达潜在的合作者吓住了,万一英特尔告了并且要求救济发布禁令的话,所有全美达的产品都不能使用。这谁还敢买呢?

问:这是什么意思?

答:意思就是英特尔诉讼全美达侵犯专利,这时候英特尔请求专利救济禁令。那么涉嫌侵犯专利的全美达产品将不能生产、出售,直到诉讼结束。

问:美国还有这么流氓的东西?意思就是我有个专利想搞创新,别人诉我我反而不能用?

答:是的。尽管发布禁令不是必须的,需要考虑衡平原则。但是直到2006eBayMercExchange案之前,美国判例一般都没有强调四要素衡平,而是大部分时候都发布禁令。诉讼官司一般比较长,全美达没法出货就完蛋了,而英特尔可以各种方式耗着直到全美达这个技术不是那么先进。这在衡平原则被强调前是大公司打击小公司常用的手段。

问:明白了。那我国呢?

答:我国在停止请求权的限制和美国相反,基本上是不会要求你停止生产,而是等待诉讼结束,该赔的赔。

问:看起来还是咱们的比较鼓励创新,对小公司友好。

答:未必,你换个角度。如果小公司做出来一个专利,大公司直接就侵权用了,那么在诉讼期间大公司的产品可能就通过倾销把小公司搞得没有销路而垮掉,最终大不了赔一笔钱嘛,但是你公司已经完蛋了。

问:看来凡事都有利有弊啊。那么Transmeta组后的命运是什么呢?

答:因为被讼棍盯上了,加上二进制翻译本身也有一定的缺陷。我就没见过不崩溃的二进制翻译系统,Curose本身技术很先进,但是出货量也受到了影响。随后的Efficeon也不太好卖,有点危在旦夕的意思,之后它就主要靠专利授权生存了(成为IP公司)。这时候,全美达在2006年对英特尔发送了诉讼,诉英特尔侵犯低功耗相关专利,这个所谓的专利核心就是我们熟知的迅驰技术。

问:全美达是才发现的吗?

答:我想不是。不过因为全美达和英特尔此时已经不能称为竞争对手了,全美达诉讼目标就是为了多挣点钱而不是禁止英特尔生产,所以它要等英特尔多用用这些技术再诉讼,也就是养肥了再杀。

问:看起来全美达也不是啥好玩意。

答:挣钱嘛,不寒碜。

问:我有个问题,为啥英特尔现在不怕全美达让法院发布禁令呢?

答:这个原因有很多。腾讯也不怕有人申请南山区发布啥禁令吧?当然还注意这个诉讼时间点,它发生在那场著名的eBayMercExchange期间,实际上就在这个诉讼前后,四要素原则就在美国法律界重新申明,法院对于禁令发布也趋于谨慎,包括I4L诉微软的XML案等。

问:明白了。那么这个诉讼最终怎么样呢?

答:英特尔随后反诉全美达侵权,这也是专利权官司常见情况。整个官司持续了一年,最终以和解告终。英特尔撤回诉讼,并且赔偿和支付专利费。全美达也答应,以后不再生产x86架构处理器(它也没能力)。

问:英特尔后来做了VLIW的处理器吗?

答:这就是被成为英特尔之耻的安腾处理器。英特尔从3264位的时候,是自己脑子不清醒,也是挖的学术界人太多了,去造RISC的安腾处理器,并且不和x86指令集兼容。

问:这不是自废武功吗?

答:是的。不过学术界当时捧它捧得厉害。安腾是绝对的政治正确处理器。它既是RISC、又是VLIW,参与者又有很多学术界的红人、而且又和HP合作,不出意外的失败了。

问:好家伙,搁着叠buff呢。

答:英特尔很快发现生态问题,于是只能求助虚拟机了。

问:好家伙,你还能绕回来。

答:英特尔造了一个从安腾翻译到x86的虚拟机,叫IA-32 Execution Layer, 来解决遗产代码问题。

问:它做了什么工作呢?

答:它的最大的工作是,它把翻译业务代码和翻译库分开了。前者称为BTGeneric,后者称为BTLib

问:这个有什么好处呢?

答:实际上库代码是不需要翻译的,不同的平台的库的接口很相似。我们只需要通过wrappernative平台的库链接上去就行了。、

问:这个思想我好像在哪儿见过?

答:就是英特尔的Houdini。英特尔的安腾做失败了,这个技术还留下来了,后来就由英特尔上海接着做了Houdini,这是一前一后的关系,据说张兆庆的博士马湘宁主持了一部分工作。HoudiniArm翻译到x86/64上面,是现在的黑产必备神器。

问:看起来英特尔还是做了很多有意义的工作的。

答:英特尔对于虚拟机的贡献在于,它和AMD一起为x86/64加上了虚拟化。

问:这是什么样的背景呢?

答:时间也不早了,先吃饭,晚上到我房间聊。