.. Kenneth Lee 版权所有 2020-2025 :Authors: Kenneth Lee :Version: 1.0 qemu概念空间分析 **************** 介绍 ==== 文档背景 -------- 本章分析qemu的概念空间,这个分析最初来自这个总结: :doc:`../../软件构架设计/在qemu中模拟设备` 但随着涉及的调研主题越来越多,很多原来可以忽略的概念都不得不去面对了,所以就扩 展成这么一个完整的分析。 前面的提到的文档写于2019年,现在读者看到的这个介绍写于2025年6月,这个时间我正 在用qemu模拟一组新的指令,以及和这组指令有关的一些设备。所以我实际上断断续续维 护这个概念空间分析已经有6年了。而我写这种概念空间分析也有十几年的实践经验了。 最后,使用AI进行辅助编程我也有一年左右的实践经验。综合这些这些体会,我想着趁着 这次重整这个概念空间,整理一下我对这种概念空间建模的方法的使用经验,做一个总结。 关于概念空间建模 ---------------- 我是做架构设计工作的,但代码也没有少写,这样导致我的设计工作不能不面对概念空间 建模的问题。因为我开发的工作面太广了,我又要看芯片的设计逻辑,又要看模拟器的设 计逻辑,看BIOS,OS,编译器,开发库,应用程序,云管理工程,Python,机器学习,这 些东西我都得看(或者修改)。因为比如你决定设计一个指令来支持机器学习的性能提升, 你不面对它们的细节,你就没法预判你的特性落地需要克服哪些障碍,那么这个特性成功 的机会也会变得很低。 但人不大可能记住那么多细节,那怎么办呢?只能是具体在需要改哪里的代码的时候, 就去增强那个部分的认识。而正如我在这里介绍的那样: `道德经直译V3介绍 `_ ,我们记住的东西,主要在我们的神经网络的“隐层”中,而不是在Token上。而隐层无法 用语言去描述(因为语言就是Token了)。所以最终我很自然的选择就是,我每次修改某 个东西的时候,我都会把它涉及的概念和关键的特征,进行一些整理,等下次我再要修改 这部分代码的时候,我就先看一次对这些概念的总结,“激发”一下自己这部分的“隐层数 据”,让自己进入修改这部分代码的状态,这些写概念总结的方法,我就称为“概念空间建 模”。 概念总结这个东西,有三个关键的麻烦点: 第一,它不精确,不精确才是它的目的。要精确,你要记住所有的代码,那才是精确。甚 至那个也不够精确,代码用不同编译器编译,得到的结果也不一样(比如用32位和64位系 统编译的行为就是不同的)。所以,就没有“严格精确”这回事。这和机器学习一样,你看 到细节就看不到整体,看到整体就看不到细节,这两者本来就不可共存。我们做概念空间 建模,就是为了激发我们的隐层,而不是重建所有的隐层,所以你写多了,它的效果就没 有了。 第二,它会发展。这个qemu的概念空间分析,中间经过了6年,很多概念,我一开始看的 代码的样子,和现在的代码的样子,其实已经和不一样了,概念本身也转义了。我刚入行 的时候,整个行业气氛都认为“代码与文档应该保持严格一致”,这种想法这些年稍微务实 的人都知道是个笑话。在特定的高安全要求的领域可能还能维持,在主流的开发中,同时 维护两套逻辑,这不符合软件现在这个发展规模的基础现实。所以,这样的概念分析就不 可能和现实完全一致。甚至,我们有些同行不一定意识到,如果概念空间建模要和现实完 全一致,这个模型的信息量就会比代码本身还大。 这有点反直觉,但事实就是:代码只需要保证在最后一个版本(HEAD)上逻辑自洽,而概 念空间建模需要保证代码的逻辑自洽,因为它通常用于维护不同的软件版本,所以它是所 有版本的立体空间的二维化。常常我们会需要跨版本描述某个概念的含义的变迁。 面对这样的信息量,如果我们把时间消耗在维持模型的正确上,我们就变成舍本逐末了。 所以,实际操作中,我就是需要改到哪里,就更新哪里的概念建模,其他地方就不管它了。 这种建模就是具体要更新哪个部分推动一次建模的,不会进行严格的全体更新。 第三,可能根本没有人会承认这个模型。这一点让我举个例子:我在单位和人讨论某个设 计怎么修改的问题,我有时会问修改的人:你这个“Agent”的语义具体是什么?然后他会 说:“我不能定义它,因为这是XX部门的YY开发和维护的,我不能代表他们”。然后我就问: “你不能定义它,你怎么就能改这个代码呢?你的代码不是要和这个Agent打交道吗?” 这就是这个问题有意思的地方:你建立模型,代表的是你对这个问题的态度,不是创造者 对这个问题的态度,他未来维护也不见得就能维持这个态度。你不能认为他不维持这个语 义,你现在就不能定义这个概念的语义。因为你如何理解一个概念的语义,会决定你如何 做出你的决策(比如编码)。 所以,这个问题对很多希望逻辑简单的人来说,很难接受,他们希望做的事情和代码一样 精确:特定的行为,总是得到特定的答案。但现实是——至少以我的经验——我也想也是很多 其他软件工程师的经验——一个概念空间建模确实帮助我们建立的思维的基础,让我们的很 多思考可以依附上去,可以让我们面对复杂的信息结构可以仍进行有效的思考。 这样的概念空间模型,既不精确,也不完全正确,而我们依赖它构造出精确而正确的代码 或者产品。我们不追求它整体的精确和正确,但我们依然会评价它某个地方不够精确和正 确,这取决于这个地方抽象的特征,是否影响我们的关键决策。 这对很多人很难接受,但口头上不接受它的人行为上实际上就是接受它的。你不肯进行纸 面的概念空间建模,只是在脑子里建立一个模模糊糊的模型,然后靠不思考,而用一组具 象去“试”结果而已,这只会让你的逻辑大厦有更多破绽,产品实现得更加糟糕。你不是规 避了问题,你只是不去看这个问题了。 一个产品要完成一个计算,这个计算可以完成在CPU上,可以完成在虚拟机上,可以完成 在OS上,可以完成在用户态,可以完成在另一台代理主机上,那么放在哪个上面是最优的 呢?这和这些主体的概念定位有关,不建立这种模型,随便都放在一个地方,当下肯定是 没有问题的。但多放一些以后呢?它们就开始逻辑冲突了。稀烂的概念空间建模,影响的 是大量逻辑的集成,它决定你的逻辑大厦能搭多高,越缺乏这种控制,你就越搭不高。你 可以说“这无法用数学证明”,但我不关心,我认为大部分实际写一段时间代码的程序员都 能体会到这一点。 而这个能力,我也认为是AI短时间内不可能取代人的关键能力。我在实现我的qemu代码和 建立概念模型的时候,经常让AI帮我进行分析,或者写部分代码。比如我会这样问它: * qemu代码中如何实现删除一个动态创建的qdev? * qemu中用qemu_irq_pulse()产生一个中断,在OS收不到,但用qemu_irq_raise()却可以 收到,为什么? * …… 这些它都能给我挺好的代码和建议的。但如果我让它给我组合所有这些代码逻辑,它就开 始胡说八道了。我觉得,一旦逻辑线多了,它的神经网络结构就是没法控制哪个权重高, 哪个权重低,或者维持住每个逻辑线都可靠这个问题了。我觉得人的神经网络在解决这个 问题上的表现还是好得多的。而且,人可能在和Token的互动上能力也是比AI好的,我们 做这种概念空间建模,说到底就是分了主题把我们关心的要素分解到多个Token组成的上 下文上,然后综合所有这些总结形成一个更大的复合逻辑结构,而AI每次只能靠有限的那 些上下文(包括思维链)来控制思考方向,就不可能形成那么大的判断结构。现在很多 MCP方案都是给LLM提供更多的逻辑结构部分来实现更大范围的控制,但这些基本上都是固 定的方法,还是没法达成人这种不断根据具体情况调整模型的效果。所以,至少以现在AI 表现出来的能力发展方向,概念建模能力才是软件工程师的核心能力,只能“别人给我确 切的要求我才能写出正确的代码”慢慢很可能就被AI代替了。 概念空间建模的正向定义 ---------------------- 形容了这么半天,我也只能反向地定义概念空间建模“不是什么”,但没法正面约定它的范 围。主要原因是,它本身的规律不多。这有点像《道德经》形容“道”,道就是“全部”,而 这个“全部”就包含我们知道的和不知道的。而定义,或者说范围,是在描述特征,我们没 法描述我们不知道事物的“特征”。所以“道曰大,大曰远,远曰逝”。非要说“道”有什么特 征,就要只能说它是“大”,大得“说不过来”,就不知道细节(远),看不见(逝)。 概念空间建模的范围比“道”小,但它有类似的特征:我们决定去研究一件事情(从而去控 制它),我们也像面对“道”一样,面对一个无限大的细节空间(想象一下qemu的全部代 码),那我们把什么细节提取出来才代表它的“概念逻辑”呢? 所以,这不是一个“对不对”的问题,而是“是否有效”的问题。你从qemu的源代码中提取细 节,这任何一个细节都是qemu的一部分,都不能说是“错”的。但看着这些细节,你真的可 以认识qemu吗?这才是关键。建模好不好,不在于提取的细节“对不对”,而是那部分细节 是否可以用来构成我们对名字的正确认识,从而支持我们做出正确的决策。这里的限制就 是我们大脑似乎也有LLM那样的Context Size(上下文长度)的问题,一次有逻辑地分析 一个问题的空间都是有限的,你没法一次“看见”qemu的全部细节。你必须在全体中做出更 好的选择。 所以,如果我需要用“大”形容“道”那样去形容“概念空间建模”,那么我的形容就是:影响 范围最广最难改变的那些细节。 这句话和“大”一样缺乏特征。所以我们再补充一些具象吧: 我修改这个qemu的时候,通常会写设计思路,这种设计思路通常包括两种: 1. 提取某个目标,还原获得这个目标的要素。比如说,我要用到qemu的报错机制了,我 也在代码中大量看到error_setg()这样的调用,但我不可能直接用error_setg()这个函 数“试一试”,因为我不知道它有什么限制,它什么情况会出现在屏幕上,什么情况会 导致退出,这些我都不知道。我就需要找出和这个error_setg相关的所有设施,看它 的线程模型,看它的错误传递逻辑。这些细节连成一片了,逻辑互相自洽了,我才能 选择在某个上下文中是用error_setg()还是error_propagate(),这种针对这个目标建 立的细节和细节的关系,在架构设计中称为一个“视图”,视图就是一种典型的概念空 间建模。 2. 根据已有的建模(比如前面这种针对已有代码的建模)概念,针对我的设计目标,描 述我设定的逻辑。比如我要做一个动态的设备,那么我选择实现一个Object,还是实 现一个Device?假定我就选择实现为一个Device,在我这种情况,我就需要为它选择 一条Bus,这条Bus不存在,我就要创建一条新的Bus,这条Bus需要支持热插拔,那我 就要实现TYPE_HOTPLUG_HANDLER接口……这个组成我的动态设备的“身份”的全部相关定 义,也构成一个“视图”,这也是一种概念空间建模。和前者不同,这是对未来的可能 现实的建模。 如果你总结一下这两个具象的特征,我看到的是: * 目标。进行建模的原因是我们要达成某个目标。言有宗,事有君。丢开目标什么都没有 意义。 * 构成逻辑。人脑判断问题的依据只有“逻辑”,而所谓“逻辑”,就是“在范围内”和“不在 范围内”的集合或者真值计算。所以,虽然我们常常没法判断庞大要素的真正逻辑关系, 但我们人脑能判断的还是只有逻辑,没法逻辑化的部分。大脑只是通过数字化把模拟信 息变成真值而已。这就好比我们没法判断一个人所有所做所为,我们会简单数字化为 “好人”,“坏人”,“靠谱”,“不着调”,然后加入我们的判断模型。当我们真的进行决策 的时候,必然用了某个逻辑——不管这个逻辑所依赖的数字化结果有多不靠谱。 * 逻辑合并。在写上面这些总结的时候,我通常会看见什么是什么,找到一组细节,然后 总结这些细节,任何维度的细节,不管它是否和其他观察冲突,也不管它是否完全真实 (比如“某人认为”这种判定)。一段时间后,我会把这些细节都放在一起,然后看看那 些逻辑是一样的,把它们合并起来,这样影响目标的关键要素就会凸显出来。这样所有 逻辑的控制要素就变得很明显了。 前面两个特征是显而易见的,最后这个也许需要更多的一点解释。 我们研究一个新问题,我们都直接带着过去经验的偏见。就好比你没有研究qemu的细节前, 你可能认为模拟指令就是通过直接的代码逻辑去修改VM状态(实际qemu是把模拟指令成组 编译成指令块来模拟运行的),认为继承了Object的Device的属性一定就是Object的属性 (实际上qemu上这两者是互相独立的)。所以,无论是你自己的思考,还是你和你的读者 进行设计讨论,这些都带着严重的错误,你的名称空间中的名称,就算和qemu里面的名字 一模一样,它也不是那个意思。 所以我们不能直接用已经已有的名字去交流,已有的名字都是偏见,不能总结它代表的那 个细节。但我们也说了,你没法用“所有的细节”去交流。除了具象,我们没有办法建立你 的概念的Token的隐层。 每个名字的信息都是有限的,最多就是这个名字和你和你的沟通者共同认知的一两个特征。 这种信息量是不足以帮助你思考的。你没有见过猫,告诉你那是“某有毛喜欢抓老鼠的哺 乳动物”,你根本建立不起对猫的正确认知。不给你足够的细节训练,你没法建立出这个 名字的概念来。所以,你必须通过细节调研去建立针对那个名字的细节概念,那些不是名 字的总结,但只有足够的这种细节才能帮助你建立概念,这才是概念建模的本质。它不是 架构定义(具象化对比一下比如《The RISC-V Instruction Set Manual(RISCV指令集手 册)》,这是RISC-V的架构定义)。后者其实只有接口,基本上是没有实现细节的(但它 有接口的全部细节),概念空间建模就好比给你看十只猫,让你形成对猫的具象认知。这 十只猫只是世间无数猫的很小一个子集,但这个具象帮助你建立对“猫”这个名字的认识, 它训练的是你的神经网络的“隐层”(这也是人脑比现在的LLM强大的地方,现在LLM——如果 用拟人的说法来说的话——它就从来没有见过猫)。 听几个字和感受足够多的细节是不一样的,看书本上说“侵略者的暴行令人发指”,和亲眼 目睹侵略者在你面前杀人的认知和判断也是不一样的。 整个概念建模的目的,不是为了让你记住“总结”,而是让你“感受”到细节,去训练你的隐 层。只是我们没法让你感受所有细节,只能让你感受“典型”的细节,所以我们先调研广泛 的细节,然后尽量合并重复的细节,让你得到更好的“感受”而已。 .. note:: 对于这个如果从细节调研开始慢慢形成最后的概念定义的过程,我还推荐读者去看看 毛泽东的《寻乌调查》,这就是一个非常明显的先调研所有的细节,然后开始总结, 变成阶级,经济,社会关系的分析的过程。 《寻乌调查》可以说是一个非常典型的《实践论》和《矛盾论》的具象。《实践论》 讨论的就是细节调研,要用实际的细节去形成概念的隐层,而《矛盾论》的重点就是 如何组织和合并这些逻辑,从而找到问题的“主要矛盾和主要的矛盾方面”。 概念空间建模的原理其实和矛盾论》的原理是一样的。不过《矛盾论》的方法更强调 找到引起对抗的两股力量(不是人的力量,而是推动变化的任何力量),它的逻辑层 次是更高的,但我这里说的概念空间建模,特别是软件的概念空间建模,是一个解决 具体领域的方法,更强调的是如何用一组概念去“总结”一个已有的,或者未来的系统。 其实这有一个更确切的形容:大胆假设,小心求证。但如果我不谈这么多细节,我也没法 之靠这八个字,给读者建立它的概念。 版本对应 -------- 最后:当前这个文档的这个版本的分析,主要基于qemu主线9.2.90。但如前所述,概念空 间建模是多版本的二维压平,所以,在需要的地方我也会做版本变迁的说明。 基础名称空间 ============ 我们先定义一些基本的名称以便后面容易说清楚各种概念,认知这个概念,我们要求读者 是个有经验的C程序员,对操作系统(特别是Linux)和计算机组成原理有计算机领域大学 毕业生级别的理解,至少用qemu命令运行过一个虚拟机,用git下载和编译过qemu。你有 这个具象基础,那至少认知下面的概念是没有问题的: Host Host表示模拟虚拟机的那个平台。这个概念比较模糊,可以表示Qemu这个程序, 也可以表示运行Qemu的那个操作系统,反正不是被虚拟的那个平台。 VM/Guest 这表示被模拟的那个平台。如果我们在X86上模拟一个RISCV的机器,X86就是Host ,而RISCV是VM或者Guest。如果我们说Host的CPU,那么这个CPU是X86的,而如果 我们说Guest的CPU,那么这个CPU是RISCV的。 Backend 这是Host中模拟Guest中某种行为的那些代码。比如我们用Qemu模拟了一张e1000 网卡,在Guest中我们要“看到”这张网卡,我们需要在Guest的OS中装e1000的 驱动,这个驱动是Guest中的。但为了模拟e1000的行为,我们也需要在qemu装 一个驱动,这个驱动我们称为这个e1000的backend,它是Qemu概念上的。 Qemu使用glib作为基础设施,所以,读者如果需要和代码细节进行对应,最好对GLib的数 据结构有基本的了解,Glib提供基本的内存,线程,链表,事件调度等基础设施的封装, 本文本身不会深入到这些概念上。 .. toctree:: :maxdepth: 2 :caption: 子主题 执行模型 qom MemoryRegion 中断 pci virtio CPU模拟 其他小设施 .. vim: set tw=78