ARTICLE · 1200827
4KB内存登月源码刷屏GitHub!程序员违抗命令改少0.1秒,竟意外救下阿波罗11号
你手机里随便一张微信表情包,体积都比人类第一次登月的全部代码还要大。
最近,工程账号 World of Engineering 在社交平台 X 上发了极短的一句话:阿波罗 11 号的源代码,现在就能在 GitHub 上直接打开。
简短一句话,迅速引发了全网数十万次围观与转发。无数年轻程序员第一次点进那个名叫 chrislgarry/Apollo-11 的仓库,看着满屏六十年前的汇编指令,当场看傻了眼。

▲ 引发全网技术考古热潮的起始短帖
比起古老的语法,藏在几万行汇编背后的生死暗线更让人冷汗直流:当年负责编写登月舱动力下降代码的 20 多岁年轻程序员,曾因为嫌麻烦,违抗接口文档擅自把一个参数改小了 0.1 秒。
谁也没想到,正是这“违规”少写的 0.1 秒,在 1969 年 7 月 20 日那个惊心动魄的夜晚,把尼尔·阿姆斯特朗和整艘阿波罗 11 号从坠毁边缘硬生生拉了回来。
01目标:用织毛衣的方法,把人类送进 4KB 内存的太空
先把时间拨回 1960 年代当时的麻省理工学院(MIT)仪器实验室接到了人类历史上最疯狂的软件工程目标:写出一套能自动控制飞船离开地球、精准降落月球表面的机载程序。
挡在工程师面前的第一道障碍,是今天任何程序员都无法想象的硬件绝境。
这台装在飞船上的阿波罗导航计算机(AGC),重达 32 千克,处理器主频只有 2.048 MHz。它全身上下只有 2048 个字(约 4KB)的可读写磁芯内存,以及 36,864 个字(约 72KB)的只读程序存储空间。

▲ 阿波罗导航计算机(AGC)与宇航员交互使用的数字显示键盘(DSKY)
4KB 内存是什么概念?今天你在电脑上新建一个空白的 Word 文档,还没打字,占用的空间就已经超过它了。
而且这 72KB 的只读代码一旦写完,在太空中一个字母都改不了。因为那时候根本没有闪存工程师把代码检查无误后,要交给雷神公司工厂里的女工,用细铜线像织毛衣一样手工穿进一个个微小的磁芯环里:铜线穿过磁环代表 1,绕过磁环代表 0。
这种物理织出来的代码被称为“磁芯绳存储器”(Core Rope Memory),当时工程师甚至半开玩笑地把它叫作“老太太内存”(LOL Memory,Little Old Lady memory)。在那个没有芯片烧录的时代,代码全靠针线一针一针缝进金属里。

▲ 如今收获约 7.5 万星标的阿波罗 11 号源码仓库,包含指令舱程序 Comanche055 与登月舱程序 Luminary099
顺便澄清一个流传很广的误会:今天我们在 GitHub 上看到的这份公共领域代码,并非 NASA 官方突然打包上传的。正如网友 @GBanksSmith 在评论区指出的那样,这是 Paul Fjeld、Ron Burkey(Virtual AGC 项目维护者)等志愿者,对着 MIT 博物馆保存的当年纸质打印清单,一页页拍照、人工敲字校对出来的活化石。

▲ 网友澄清源码并非 NASA 直接上传,系志愿者根据 MIT 博物馆纸质档案逐页录入
为了在这台一旦缝好就绝不能出错的机器上活下来,MIT 团队采取了近乎冷酷的行动准则。打开指令舱源码里的 CONTRACT_AND_APPROVALS.agc,你会看到软件负责人 Margaret Hamilton(玛格丽特·汉密尔顿) 在 1969 年 3 月 28 日签下的名字。那时候全世界觉得写代码只是文员干的杂活,是汉密尔顿团队硬生生造出了“软件工程”(Software Engineering)这个词,把每一次提交、每一行审批都直接刻进了源代码里。

▲ 直接写在源代码文件里的合同批准页,提交人为 Margaret H. Hamilton
02第一重风浪:被阀门骗了的代码,与经典的“烧吧宝贝”
1966 年,刚从波士顿大学毕业的毛头小子 Don Eyles(唐·艾尔斯) 走进 MIT 仪器实验室。他接手的任务,是编写登月舱最要命的动力下降与点火代码。
很快,太空给他上了残酷的第一课
1968 年 1 月 22 日,阿波罗 5 号带着第一艘无人登月舱 LM-1 升空,目标是测试下降发动机在太空中的首次点火。原计划发动机要熊熊燃烧 38 秒结果点火仅仅几秒钟,机载计算机突然判定“发动机失效”,啪的一声直接把发动机强行关停了!
是代码写出了 Bug 吗?Don Eyles 翻开自己参与维护的 SERVICER.agc,盯住了里面的推力监控程序 DVMON。这段代码逻辑极其严谨:发动机点火后,程序每 2 秒检查一次飞船的速度增量(ABDELV),如果达不到设定阈值 DPSTHRSH,就倒计时切断引擎。
这场乌龙源于硬件团队瞒着软件团队改了操作,代码本身毫无瑕疵。发射前,硬件工程师怀疑燃料阀门有点漏,为了防爆,临时决定把打开阀门充注管道的时间从“点火前几秒”推迟到了“点火那一瞬间”,却忘了告诉写代码的 Eyles!结果燃料在空管子里多跑了几秒钟,推力爬升慢了半拍,忠心耿耿的 DVMON 当场把正常点火当成故障给掐灭了。
这次教训之后,Eyles 彻底重构了点火时序逻辑,写出了今天 GitHub 上最火的那个文件:BURN_BABY_BURN--MASTER_IGNITION_ROUTINE.agc(烧吧宝贝烧吧,主点火例程)。

▲ 以 1960 年代黑人电台 DJ 口头禅命名的主点火例程 BURN_BABY_BURN
他不仅用当年摇滚电台 DJ 的狂野口头禅给点火程序命名,还在严肃的汇编代码表头写下了两句拉丁文警告:“愿恶念者蒙羞”和“勿触(别乱改我的表项)”。
03第二重风浪:1202 警报背后,那个改少 0.1 秒的救命参数
1969 年 7 月 20 日,阿波罗 11 号登月舱“鹰号”开始向月面下降。
全世界都知道当时发生了著名的 1202 和 1201 程序警报。因为交会雷达开关位置不对,雷达疯狂向计算机发送中断信号,把那可怜的 4KB 内存工作区(Core Sets 与 VAC 区域)彻底塞爆了。
关键时刻,汉密尔顿团队设计的优先级调度架构与 ALARM_AND_ABORT.agc 救了全场:计算机依靠毫秒级自动软重启避开了死机,果断扔掉低优先级的雷达和显示任务,死死保住最高优先级的发动机控制。

▲ 负责处理非中止类异常的 ALARM_AND_ABORT.agc 源码

▲ 网友评价汉密尔顿团队在编写阿波罗代码时推动了“软件工程”概念的普及
但当时在场的阿姆斯特朗和地面指挥中心都不知道,就在 1202 警报闪烁的同时,死神其实正从另一个方向伸出手,而挡住死神的,竟然是 Don Eyles 当年犯下的一个“违规操作”。
要让登月舱平稳落地,软件必须实时调节发动机节流阀的推力(在 10% 到 94% 之间变化)。因为发动机收到指令后需要一点时间才能改变推力,Eyles 在 THROTTLE_CONTROL_ROUTINES.agc 里写了一个滞后时间补偿参数,叫作 THROTLAG。
这就像你在酒店调冷热水龙头:如果水管要延迟 3 秒才出热水,你就得提前算好这 3 秒的提前量。
当时官方接口控制文档(ICD)白纸黑字写着:下降发动机的硬件响应滞后时间是 0.3 秒。按规矩,Eyles 必须在常量文件 CONTROLLED_CONSTANTS.agc 里填上 DEC +30(代表 0.3 秒)。
但 Eyles 在模拟器上跑测试时发现,填 0.2 秒就足够稳住推力了。这位二十出头的年轻人嫌补偿太多弄脏了代码,转头对搭档 Allan Klumpp 说了一句名言:“这就像吃药一样,既然吃两片能好,干嘛非要按说明书吃三片?” Klumpp 尊重了这位年轻搭档的直觉,任由他在最终飞向月球的源码第 130 行写下了:
THROTLAG DEC +20(即 0.2 秒)。
直到阿波罗 12 号飞完,工程师回查遥测数据时才惊出一身冷汗!原来阿波罗 11 号和 12 号在最后手动着陆阶段(P66),发动机推力一直在发生幅度高达 25% 的剧烈方波震荡(因为波形像城堡的锯齿墙,被称为“城垛振荡”)。
为什么会剧烈震荡?两个隐蔽的物理陷阱叠在了一起:
- 传感器装偏了(IMU 点头效应)
:惯性测量单元装在登月舱重心上方偏前 4 英尺处。阿姆斯特朗最后时刻推杆调整姿态时,机头上下点头的离心力被传感器误判成了飞船在垂直坠落! - 硬件改快了却没改说明书
:发动机制造商私下改进了阀门,把真实的物理响应延迟从 0.3 秒缩短到了 0.075 秒,却根本没更新给软件团队的那份文档!
这就好比热水器早就换成秒出热水的新款了,说明书上还写着要等 3 秒。结果 Eyles 写的 0.2 秒其实已经属于过度补偿了。事后数学建模证明了一个令人心惊的事实:如果当年 Don Eyles 乖乖听话,按官方文档把 THROTLAG 写成“正确的” 0.3 秒,阿波罗 11 号的节流阀控制回路将在数学上彻底崩溃!
一旦失稳,下降发动机将在最小推力和最大推力之间疯狂抽搐。在 1202 警报灯狂闪的背景下,休斯敦地面指挥中心绝对会以为计算机彻底疯了,毫无疑问会按下紧急中止键。人类首次登月,将因为一份没及时更新的说明书而功亏一篑。
这就是硬核工程里最毒辣的真相:比起糟糕的代码,“文档腐败”往往更容易摧毁复杂系统;当纸面规范脱离了真实的物理世界,盲从规范就是自杀,唯有敬畏实测数据才能保命。
04第三重风浪:61 次按键,在太空中“黑”进只读铜线代码
如果说阿波罗 11 号靠的是年轻人的实测直觉,那么 1971 年 2 月的阿波罗 14 号,则是人类软件史上最巅峰的一次“空中热补丁”极限操作。
距离阿波罗 14 号登月舱点火下降只剩最后两个小时。休斯敦地面遥测突然抓到了一个致命信号:驾驶舱面板上的“紧急中止按钮”(ABORT Switch)竟然自己接通了!
宇航员伸手敲了敲面板,信号消失了原因很快查明:开关内部有一颗脱落的微小焊锡球,正在失重状态下到处乱飘。一旦两小时后下降发动机点火、箭体剧烈震动,这颗焊锡球只要再碰一下触点,就会接通硬件通道 CHAN30 的第 1 位(BIT1)。
当时负责监控这个按钮的,正是 Don Eyles 写的 P70-P71.agc 例程。它每 0.25 秒检查一次:只要发动机已经点火(LETABORT 标志位置 1),且检测到 BIT1 接通,计算机就会二话不说执行 P70A 紧急中止,直接砍断下降段发动机,点燃上升段火箭把宇航员弹回轨道!
怎么办?障碍几乎无解:
P70-P71.agc是用铜线织死在只读磁芯绳里的,根本没法删掉那几行检查代码; 你也不能等点火之后再让宇航员手敲键盘清除 LETABORT标志位,因为点火瞬间程序会自动把LETABORT置 1,在宇航员敲完键盘前的那几秒钟震动窗口期里,焊锡球一碰,飞船照样完蛋!
全世界都在看 MIT 实验室里的 Don Eyles。绝境之中,Eyles 死死盯着自己几年前写的汇编清单,突然在 P70-P71.agc 的第 73 行看到了一个绝妙的逻辑漏洞:
程序在去读那个坏掉的硬件开关之前,会先读一下可读写内存里的当前状态寄存器 MODREG,问自己一句:“我现在是否已经在执行 P71 中止程序了?(P71NOW?)” 如果是,它就直接跳走去刷新屏幕(BZF LANDISP),根本不去管那个硬件开关!
“那就骗过我自己的代码!”
一套惊世骇俗的行动方案在半小时内出炉:
- 第一步(指鹿为马)
:在发动机点火之前,让宇航员用键盘直接修改 4KB 内存里的变量,强行把当前模式 MODREG改成71。这样点火瞬间哪怕焊锡球乱撞,计算机一看MODREG = 71,以为自己“已经在逃命了”,直接跳过开关检查! - 第二步(手动接管与拆弹)
:因为骗了计算机,自动推力爬升和制导方程也不会自动启动了。所以点火 26 秒后,宇航员必须手动推满节流阀、手动发指令启动制导,接着趁飞船平稳把 LETABORT标志位清零,最后在雷达锁定前把MODREG偷偷改回正常的63!
距离点火只剩最后 30 分钟,休斯敦通过无线电把这串指令念给飞船。登月舱驾驶员 Edgar Mitchell 在那个简陋的 DSKY 数字键盘上,像弹钢琴一样一字不差地手动敲完了整整 61 次按键。
焊锡球被完美绕过阿波罗 14 号稳稳落在月球弗拉·毛罗高地。
05为什么 57 年后,我们依然对这份代码顶礼膜拜?
今天,如果你随便打开一个现代软件的源码库,搜一下 TODO 或 FIXME,往往能搜出成百上千条“以后再改”的烂摊子。但在阿波罗 11 号那几万行汇编里,网友 @bepsays 搜遍了整个仓库,没有找到哪怕一个 TODO 或 FIXME。
在那个算力荒漠的年代,一群平均年龄二十多岁的工程师,用不到一张表情包大小的内存,靠着精妙绝伦的优先级抢占、精确到毫秒的寄存器算计,以及在危机面前对自己每一行代码分支的了如指掌,把人类送上了另一颗星球。
今天我们点开 GitHub 上的 chrislgarry/Apollo-11,透过那些古老的汇编语法,依然能触摸到软件工程诞生之初最纯粹的灵魂:当每一行代码都必须用铜线亲手缝进磁环、当每一个参数都直接连着两条人命时,人类究竟能把代码写得多么极致、多么优雅。















































































