XMAKYNA Blog

EngineeringFoundations

计算机如何工作:费曼的高速档案员

Language: Simplified Chinese
Illustration of a filing clerk at numbered drawers, surrounded by diagrams of instructions, addresses and repeated steps, representing Feynman’s model of computer operation.

理查德·费曼是科学史上的传奇人物,但仅凭这一点,还不足以解释我们为什么用这场讲座开启 XMAKYNA 的技术博客。我们从这里开始,是因为许多我们关心的工程问题,最终都会落到几个极其简单的问题上:机器读了什么?存了什么?下一步做了什么?它被允许影响什么?费曼的“档案员”模型给了我们一套讨论这些问题的共同语言。后面的文章可以直接建立在这个基础上,而不必每次都从头解释。

四十多年后的今天(这场研讨会录制于 1985 年),这堂讲座依然出奇地新鲜。几乎任何对计算机感到好奇的人都能从中有所收获,因为费曼并不是从术语开始。他给我们一个档案员、一排编号抽屉、一些简单指令、一张告诉档案员下一步去哪里的便条,以及可以指向其他抽屉的数值。仅凭这些材料,计算机最基本的运作方式就开始显现出来。

无论这堂讲座还是这篇文章,都没有解释计算机的一切。它们真正解释的是一个有用的核心:信息存放在某处,指令决定接下来发生什么,存储的值可以把机器指向别处,而简单操作可以组合成复杂得多的行为。

这对安全也很重要。许多严重故障一旦还原成这幅图景,就没有那么神秘了。机器可能从错误的位置读取,走到错误的下一步,信任一个在别处其实有不同含义的值,或者被某个能够影响抽屉内容或档案员下一步查看位置的人故意引向别处。

机器可以完美地遵照自己的指令,却仍然做出危险的事情。理解这一点,是我们以后会反复使用的基础之一。

在大型语言模型(LLM)以及其他输出看起来能力惊人的系统已经成为日常现实的今天,这堂讲座听起来又有了不同的意味。费曼讲的不是现代人工智能,这篇文章也不会假装他讲的是。它给我们的东西更根本:面对令人惊叹的输出时,仍然去问机器实际上在做什么、它怎样到达这个结果、它可能在哪里出错,以及这些错误一旦变得重要会造成什么后果。对我们而言,这也是使用 AI 时一个核心工程问题:代码能够成功构建(通过编译)、能够运行、看起来也合理,仍然不够;我们还必须确认它确实正确。

因此,这篇文章有意从基础开始。我们会沿着费曼的简单比喻走得足够远,让非专业读者也能形成一幅牢固的图景:计算机如何找到信息、遵循指令、改变方向,并从简单步骤中产生复杂行为。以后我们还会一次又一次用到这幅图景。

先从一个档案员和一些抽屉开始

想象一个档案员待在一间装满编号抽屉的房间里。每个抽屉都可以放一个数字或其他简单信息。档案员可以打开抽屉,读出里面的内容,复制一个值,把新值写到别处,比较两个值,再按照一条指令决定下一步做什么。

Diagram of a filing clerk with a next-instruction note, numbered drawers containing values and an instruction, and four literal operations: read, write, compare and follow next.Diagram of a filing clerk with a next-instruction note, numbered drawers containing values and an instruction, and four literal operations: read, write, compare and follow next.
图 1. 档案员模型:编号抽屉保存数值,而一小组按字面执行的操作负责读取、写入、比较,并继续执行下一条指令。

这种设定故意毫不起眼。档案员不需要知道某个数字表示金钱、颜色、字母、温度、棋盘上的位置,还是另一个抽屉的地址。意义来自我们赋予数据的表示方式。对档案员来说,只有数值、位置和过程。

这是给计算机祛魅最有用的方法之一。机器从外面看可以表现得惊人,而构成它的单个操作却简单得近乎令人发笑。复杂性来自组织方式:存了什么、怎样表示、下一条运行的是哪条指令,以及有多少这样的操作能够串联起来。

程序接下来走哪条路,也存了下来

真正有力量的一点是,档案系统不仅能描述数据,也能描述工作接下来走哪条路。档案员总要在某处记录下一条该执行哪条指令。在现代计算机体系结构中,这一小块状态通常叫作程序计数器。这个名字听起来很技术,但背后的想法简单得近乎滑稽。它就像档案员手里的一张便条,上面写着:“下一步读取第 101 条指令。”

执行完第 101 条指令后,通常可能继续到 102,再到 103、104。但一条指令也可以说:不要继续读下一张卡,改去执行第 250 条。这就是跳转。条件跳转再加一个判断:如果这个值为零,就去 250;否则继续到 102。

这里没有发生任何神秘的事情。机器只是改变了“下一条指令所在位置”这个被存储的值。可就是从这一个想法出发,我们得到了分支、循环、重试,以及程序的大部分常见形状。所谓循环,不过是一次跳转,把档案员送回已经执行过的指令,直到某个条件发生变化。

这一点值得多停留片刻,因为它把大量计算机术语还原成了普通的记账工作。机器的控制流本身就是状态。“下一步发生什么?”这个答案被表示在某个地方,而指令可以改变这个答案。

Diagram showing instructions 101 to 104 in sequence and a conditional test that changes the stored next instruction to 250 or 103.Diagram showing instructions 101 to 104 in sequence and a conditional test that changes the stored next instruction to 250 or 103.
图 2. 控制流本身就是状态:正常路径逐条执行指令,而一个条件可以改变被存储的“下一条指令”,从而把执行引向别处。

指针不过是记下来的一个位置

指针也可以用同一个档案系统来解释。假设 500 号抽屉里放着数字 914。有时,914 就只是九百一十四这个数字。换一个上下文,它也可以表示:你要找的东西存放在 914 号抽屉。当一个数字这样使用时,它就在充当指针,也就是一个指向另一个位置(另一个抽屉)的地址。

力量仍然来自解释方式。档案员不会在内存里看到一支发光的箭头。档案员读到一个值,当前指令要求把它当作位置,下一步操作便用它去选择另一个抽屉。因此,一个被存储的数字就可以改变后续工作从哪里读取、又向哪里写入。

Diagram showing drawer 500 storing 914, which can be ordinary data or treated as an address that redirects the next read to drawer 914.Diagram showing drawer 500 storing 914, which can be ordinary data or treated as an address that redirects the next read to drawer 914.
图 3. 指针把间接寻址直观地表现出来:500 号抽屉里存着 914;把 914 当作位置使用,下一次读取就会转向 914 号抽屉。

这也是档案员模型能够扩展得如此好的原因之一。数据可以给其他数据命名,指令可以把执行引向其他指令。一小组作用于位置的操作,就能搭出行为远比这些基本操作本身丰富的结构。

简单指令,巨大行为

读一个抽屉。写一个抽屉。把两个值相加。比较它们。把一个值当作另一个值所在的位置。改变下一条执行的指令。重复。单独看,这些动作都谈不上惊人。

但如果把它们放到一个巨大的档案系统中,以极高速度组合起来,它们就能实现电子表格、编译器、游戏、数据库、图像解码器、网页浏览器和操作系统。档案员不需要为每一种应用再拥有一种神秘的新能力。我们只需安排数据和指令,让同一套基础机器产生不同的行为。

Diagram showing simple operations such as read, write, add, compare and redirect repeated millions of times to produce larger behaviours such as spreadsheets, compilers, databases and operating systems.Diagram showing simple operations such as read, write, add, compare and redirect repeated millions of times to produce larger behaviours such as spreadsheets, compilers, databases and operating systems.
图 4. 在大量状态上以极高速度串联简单操作,可以产生复杂行为;速度放大的是过程的规模,而不是它的判断力。

这正是这个解释精彩的地方。它并没有把计算机说得微不足道。相反,正因为如此强大的能力竟由如此朴素的材料构成,计算机的力量反而更令人印象深刻。

当档案员开始显得聪明

费曼最终把档案员模型推向一个更难的问题:机器能思考吗?在他的表述里,困难并不在于计算机永远不可能执行某种思考过程,而在于人的思考并没有一套已知而且完全确定、可以直接交给档案员执行的程序。

在进入象棋之前,费曼先用算术提出了同一个观点的简单版本。机器并不是在模仿数学家计算时的体验。它使用的是另一套机制,而那套机制更适合例行算术。

“它们做算术比任何人都好,快得多,而且方式也不同。……我们永远不会为了让它们看起来像人一样计算,就去改变它们做算术的方式,那是在倒退。因为人做算术很慢、很繁琐、很混乱,而且充满错误。”

费曼这里针对的是算术,而不是人类智能总体。他讲的是执行方式:对于例行算术,计算机采用不同方法本身就是优势。只为了让它看起来像人,就让它模仿人的过程,反而会把这个优势丢掉。

这就引出了更难的问题。如果机器做算术不必走人的路线,那么智能行为也必须通过人的路线产生吗?

象棋让这个难题变得具体。一个强大的人类棋手似乎并不会枚举数百万个局面,而是会看出结构:一个双攻、一个薄弱格、一种熟悉的形状、一条有希望的变化。费曼提醒我们注意这样一个缺口:说“人看到了某个模式”很容易,但要真正说明这个有用的模式最初是通过什么机械过程被发现的,却完全是另一回事。

计算机可以用另一种方式处理同一个问题。它能够检查远多于人的局面,应用明确的规则和评估,并利用启发式方法,避免在所有地方投入同等的计算量。它不需要重现人类专家得出同一步棋时那条私人的思考路径。

费曼把这个区别说得很漂亮:“我们没法让它像人那样下棋,但可以让它下得比几乎所有人都好。”(着重为我们所加)

这句话承载着一个更大的工程思想:相像和有用不是一回事。机器不必复制人的路径,也能产生我们会认作“智能”的结果。真正重要的是它实际执行了什么过程,它能利用哪些模式或规律,它怎样搜索或在可能性之间作出选择,以及它的能力到哪里为止。

因此,即便看起来已经跃入“智能”的领域,档案员模型依然成立。结果可以显得异常聪明,而底层机器仍然只是在操作各种表示、比较不同选择,并按照明确操作在状态之间移动。神秘并没有转移到硬件里。真正变难的是:有用的结构怎样被表示,过程怎样找到它,以及计算本身可以涌现出多少能力,而不必要求机器像人一样思考。

讲座后半部分谈到的思考机器、智能、启发式方法及其局限,远比本文覆盖得更广。不过,即使把范围收窄到这里,费曼最后一句话仍值得保留下来:“我认为我们正在接近智能机器,但它们正在表现出智能所必然带有的弱点。”

今天再听这句话,很难没有不同的感受。它允许一台机器真正有用,甚至能力非凡,而不要求它完美无误。弱点并不会自动否定智能;它只是告诉我们,智能本身可能有边界、有盲点,也有自己的典型失效方式。对工程来说,这比假装“有用的机器智能必须先变得绝不犯错”是更有价值的起点。

档案员只按字面执行

这种智能本身也有锋利的一面:底下那个档案员仍然只按字面执行。档案员遵循的是实际存在的过程,使用的是实际存在的状态。它不会替我们修正意图。

如果一条指令指向错误的抽屉,档案员不会神奇地知道我们原来想指哪一个。如果 500 号抽屉里是 915,而我们以为会是 914,后面的指针就可能把工作带到别处。如果跳转条件写错了,档案员可以完美地走进错误分支。如果系统的一部分把一个值理解为“大小”,另一部分却把它理解成别的东西,档案员也不会因为这种不一致看起来可疑就停下来。

我们说档案员“糊涂了”,并不是说计算机产生了人的困惑。意思是,实际状态、表示方式或过程已经不再符合设计者以为系统正在执行的模型。机器仍然可以完全服从,而整个系统却已经错了。

真实系统里也可能有不止一个档案员在操作同一批抽屉。一个档案员读取某个值时,另一个可能正在修改它、清空它,或者把那个抽屉改作他用。第一个档案员随后可能继续使用旧值,甚至顺着一张便条走到一个已经不再属于它原先所指对象的抽屉。只要有多个档案员能够触及同一批抽屉,谁能在什么时候读取或修改它们,就会成为过程是否正确的一部分。

工程从这里开始变得有意思

许多棘手的软件故障,都可以还原成这个问题的某种变体:沿着错误的位置走了;判断时使用一种表示,执行动作时却使用另一种;某个值被检查之后,在使用之前又发生了变化;过期记录被当作当前记录;回退路径把工作送上另一条路线;后一个阶段信任了前一个阶段从未真正建立过的含义。

从外部看,这些故障可能发生在非常复杂的软件里。可如果从内部往外看,问题常常具体得令人愉快:档案员读了哪个值?这个值指的是哪个抽屉?接下来执行了哪条指令?哪个条件触发了跳转?下一条指令以为自己收到了什么状态?

这是我们重视的一种工程习惯。只要系统层面的说法开始变得模糊,就继续往下追,直到机制重新变得可以按字面描述。

安全问题也从这里进入

普通 bug 可能会意外地把档案员带入混乱。安全问题出现的可能性,则来自有人能够有意影响状态或指令,让机器走向一种有用的结果。

攻击者并不需要让计算机变得“不听话”。恰恰相反,一些最有意思的故障正是因为计算机太忠实地服从:它沿着被攻击者影响过的指针走,接受误导性的表示,执行被允许的跳转,完成已获授权的操作,或者完全按照过程的规定把一个值交给下一个组件。

Diagram showing wrong location, wrong meaning or wrong route feeding literal execution, where instructions are followed exactly yet produce a dangerous result.Diagram showing wrong location, wrong meaning or wrong route feeding literal execution, where instructions are followed exactly yet produce a dangerous result.
图 5. 按字面完全服从并不保证正确:即使每条指令都被准确执行,错误的状态、含义或路径仍可能导致危险结果。

后果取决于档案员被允许做什么。在一块可丢弃的临时区域里发生混乱,可能什么都不会造成;同样的混乱如果靠近凭据、持久状态、私有数据、网络访问能力或另一个高权限组件,意义就完全不同。

这些区别以后还会再谈。本文不是要提前讲完它们。它的目的只是给我们一个共同的心智模型:在争论某个安全属性叫什么之前,先问有哪些信息、它们是什么意思、指向哪里、下一条运行什么指令,以及从那里能够触及什么权限。

一个我们会反复使用的模型

我们预计“档案员”会在 XMAKYNA 的文章中反复出现,因为它能让困难概念变得易懂,却不会把它们讲得幼稚。指针变成了记下来的一个位置。跳转变成了对“下一条指令”便条的修改。程序计数器就是档案员的书签。内存变成抽屉。执行过程则变成对这些抽屉进行的一连串微小操作。

有了这幅图景,更高级的论证就有了坚实的落脚点。我们可以讨论错误状态、过期状态、误导性表示、重定向、权限,以及攻击者能够操纵的混乱,而不必假装计算机拥有人的判断,也不必把抽象概念的名字本身当成机制的解释。

当困难的工程问题开始显得比实际情况更神秘时,这个模型尤其有用。去掉规模和陌生感,从某种意义上说,它仍然是一只档案柜:把状态、表示和过程弄对,机器就会照着把工作做完。

所以,这堂讲座不只是一段迷人的老式计算机解释。它给了我们一种可以长久使用的思考方式。

讲座信息与命名说明

主讲人:Richard P. Feynman。链接的录音本身显示日期为 1985 年 9 月 26 日,并将场合标为美国加利福尼亚州大苏尔 Esalen Institute 的 Idiosyncratic Thinking 研讨会。

这场讲座曾以不同标题流传,一些出版资料对相关 Esalen 材料的日期也有不同记载。这里链接的上传版本标题为 Hardware, Software and Heuristics;Computers From the Inside Out 和 The Feynman Lecture on Heuristics 也见于其他地方。本文统一把这场讲座称为 Computers From the Inside Out。

观看讲座:Richard Feynman, Computers From the Inside Out (YouTube)。

归根结底

把计算机一层层拆解到足够基础,最终会留下一个出奇耐用的图景:一个速度极快、严格按字面执行的档案员,面对数量事实上极其庞大的编号抽屉,并掌握一小套操作。一个值可以给另一个抽屉命名;一条指令可以改变下一条要执行的指令;程序计数器不过是档案员的书签;跳转就是改动这枚书签。极其复杂的行为,正是这些简单动作在惊人速度下反复执行的结果。

这个模型简单,却并不浅薄。它提醒我们:机器依据的是我们实际编码进去的状态和表示,而不是我们希望它自行理解的意图。档案员被带入混乱,是正确性问题;如果攻击者能够有意制造这种混乱,而后面又连接着可利用的权限,那就是安全问题。我们以后还会不断回到这个想法。