距离月面三万英尺的 1202 警报:阿波罗 11 号是如何被 64KB 内存救活的

1969 年阿波罗 11 号登月舱在距离月面仅 33,000 英尺处爆发出未知的 1202 警报。这不是代码 Bug,而是一场硬件电气冲突引发的算力过载风暴。解构 MIT 如何用 64KB 手织内存和异步优先级调度器挽救登月。

分享
距离月面三万英尺的 1202 警报:阿波罗 11 号是如何被 64KB 内存救活的

着陆前三分钟,雷达接口硬件故障引发中断风暴;MIT 写的优先级调度器如何让“鹰号”登月舱起死回生

Apollo 11 Lunar Module Eagle

当我在 GitHub 上翻开阿波罗制导计算机(AGC)开源的导航源码、顺着函数调用去复盘著名的 1202 警报时,我原本以为会看到一段由于数值浮点精度溢出导致的轨道计算 Bug。

但我真正看到的,是一堂足以给今天所有云架构师上课的分布式高可用系统实战。

1969 年 7 月 20 日,任务时间 102 小时 38 分 22 秒。

阿波罗 11 号登月舱“鹰号”正以每秒二十英尺的速度穿过月球静海上方 33,500 英尺的夜空。狭窄的舱室里没有座位,阿姆斯特朗和奥尔德林用弹簧拉绳把自己固定在舱室地板上。下方的下降发动机正在全功率喷射,每秒吞噬 80 磅推进剂。阿姆斯特朗正死死盯着燃料表,突然,主仪表盘亮起了一盏刺眼的黄光。

数字键盘屏幕上,两行惨绿色的荧光数字开始闪烁:1202

“程序警报,”阿姆斯特朗向休斯顿呼叫,声音明显紧绷,“是一个 1202。”

舱内的两位宇航员完全不知道 1202 意味着什么。二十四万英里外的休斯顿控制中心,26 岁的导航控制官 Steve Bales 只有不到三十秒的时间做出生死决断:这台机器到底是在正常计算、系统崩溃,还是马上要引爆炸药把登月舱弹回太空、彻底放弃人类首次登月?

Apollo DSKY Interface

阿波罗 DSKY :显示与键盘交互的终端。公有领域,来自 Wikimedia Commons。

闪烁在三万英尺处的绿色警报

1202 不是普通的报错,而是一台快被数据活活撑死的计算机发出的呼救信号。

当时登月舱正处在第 63 阶段动力下降(P63)。在这个阶段,计算机必须同时处理复杂的惯性积分、采集着陆雷达的多普勒测距信号、实时计算推进剂消耗后的舱体质量,并以每秒 40 次的高频向发动机平衡环发出姿态纠偏脉冲。

每一个动作都在疯狂争抢 CPU 周期。而这台正在控制登月舱的计算机,内存容量大概只有你手腕上的智能手表的百万分之一。

当屏幕跳出 1202 时,计算机其实在说:我的工作区缓冲内存已经彻底耗尽,再也无法接纳新的任务了。

如果制导计算机死机哪怕两秒钟,下降发动机就会失去矢量平衡。整艘飞船将在全推力下失控翻滚,宇航员唯一的选择就是按下红色自毁分离键,抛弃登月舱逃生。

倒计时在流逝,休斯顿必须在几秒钟内给出答案。

纺织女工手织出来的只读内存

要想明白这套系统为何没有崩溃,必须先看看它运行在怎样的苛刻硬件躯壳里。

阿波罗制导计算机(AGC)由 MIT 仪器实验室在 Charles Stark Draper 的带领下设计。在 1965 年研制能自主导航的航天级电脑,无异于一场疯狂的赌博。当时能胜任类似计算的 IBM 大型机需要占据一整间带空调的大机房,并消耗几十千瓦电力。

MIT 把 AGC 塞进了一个只有 1 立方英尺、重 70 磅的密封铝合金盒子里,整机功耗仅为 55 瓦

Apollo Guidance Computer Core Rope Memory

阿波罗制导计算机的绳芯只读内存模块。公有领域,来自 Wikimedia Commons。

它的核心参数在今天看来简陋得令人难以置信:

  • 时钟主频仅仅 1.024 MHz
  • 只有 2,048 个字(Word)的可读写内存(RAM,擦除存储器)
  • 以及 36,864 个字的只读存储器(ROM,固定存储器)

那 36K 的只读代码,是用一种今天早已失传的手艺制造出来的——Core Rope Memory(绳芯内存)

在麻省的纺织工厂里,女工们拿着长长的探针,手工把铜线穿过一个个环形铁氧体磁芯。铜线穿过磁芯中心代表二进制的 1,绕过磁芯外部代表二进制的 0。

代码是用铜线一圈一圈“织”出来的。这赋予了系统无可匹敌的物理抗干扰性——宇宙射线或电压尖峰绝不可能改变手织线圈的位置。但也意味着,火箭一旦竖立在肯尼迪航天中心的发射台上,代码就变成了物理化石,绝不可能打任何补丁。

所有抵御灾难的逻辑,必须在点火之前,完整塞进那可怜的 2,048 个字的可读写 RAM 里。

Hal Laning 的异步优先级调度器

在 1960 年代的大型机领域,所有的软件都在跑同步批处理:任务 A 跑完,再跑任务 B,接着跑任务 C。

但在登月舱里,这种机制会要人命。如果宇航员按下一个查询按钮,计算机绝不可能暂停发动机的推力控制循环,去傻傻地等待用户输入。

MIT 实验室一位性格内向的数学家 Hal Laning,为 AGC 设计了一套超越时代的底层内核:异步优先级抢占式执行器(Asynchronous Priority Executive)

在 Laning 与 Margaret Hamilton 的主导下,AGC 彻底摒弃了死板的循环轮询,将系统划分为两大子组件:Waitlist 与 Executive

Waitlist 负责处理耗时小于 4 毫秒的硬实时定时任务,比如每隔固定微秒采集一次加速度计读数,或者向姿态喷管发出点火脉冲。

Executive 则负责长期、复杂计算的任务。它维护一个最多可容纳 8 个并发任务的优先级队列。每个任务被赋予了明确的优先级:

  • 优先级 30:姿态控制与发动机推力伺服回路
  • 优先级 20:雷达测距与着陆轨道积分计算
  • 优先级 10:键盘输入扫描与 DSKY 屏幕数字刷新

每次处理器执行完一条底层机器码,Executive 就会迅速扫一眼任务队列。只要有更高优先级的任务就绪,系统立刻暂停当前任务,把它所有寄存器状态暂存进一个叫 VAC Area(向量累加区) 的临时内存块里,实现无缝的上下文切换。

这是一套在 2KB 内存上运行的抢占式多任务操作系统。

并非代码 Bug 的 15% 硬件风暴

当阿姆斯特朗呼叫 1202 警报时,其实代码本身并没有写错任何逻辑。

引发这场灾难的,是一个隐藏在驾驶舱仪表板背后的电气工程乌龙。

在下降过程中,奥尔德林严格按照纸质检查清单,把 会合雷达(Rendezvous Radar) 的旋钮拨到了自动跟踪模式。这台雷达根本不是用来着陆的,它的作用是在登月舱万一发生危险逃逸时,追踪头顶60英里外的指令舱。

奥尔德林提前通电预热,是为了在发动机意外熄火时能瞬间锁定逃生路线。

然而,谁也没料到的是:会合雷达的供电模块连接的是一套 800 赫兹的交流励磁电源,这套电源与计算机内部的时钟基准之间存在 30 度的电气相位差

这个小小的相位差,导致雷达接口硬件不断产生虚假的角位置信号。接口电路像抽风一样,以每秒数百次的频率向计算机狂扔硬件中断。

每一次硬件中断都会强制 CPU 停下手头的工作,分配一个 VAC 内存缓冲区来处理一个根本不存在的雷达读数。

这股电流噪声,凭空吃掉了这台 1MHz 计算机 整整 15% 的处理周期

在极其紧张的着陆临界点上,15% 的算力被盗是致命的。未完成的任务像积雪一样堆死在队列里。

终于,Executive 试图为一次关键的着陆推力计算分配 VAC 内存块时,发现系统内所有的 8 个槽位全部被占满了。

调度器无处可退,于是在屏幕上输出了程序警报代码 1202——EXECUTIVE OVERFLOW: NO VAC AREAS

Mission Control during Apollo 11

阿波罗 11 号任务期间的休斯顿任务控制中心。NASA 档案照片。

代码是如何选择活下去的

接下来发生的事情,将成为计算机工程史上最震撼的自愈时刻。

当 Executive 发现 VAC 内存枯竭时,系统不会直接卡死,也不会陷入死循环。它触发了一道预埋在底层的防卫机制:Restart Trap(重启陷阱)

在短短几个毫秒内,AGC 自动触发了热重启。

处理器没有彻底断电,而是瞬间清空了 RAM 工作区,将整个任务队列一扫而空。它干脆利落地扔掉了所有积压的雷达中断处理,扔掉了显示器刷新线程,砍掉了低优先级的后台扫描。

紧接着,根据写在 Core Rope 只读内存里的核心恢复向量,Executive 仅重新拉起了保障飞船不会坠毁的三项最高优先级任务

  1. 重新接入姿态喷管推力控制循环;
  2. 重新采样惯性测量单元(IMU)陀螺仪数据;
  3. 重新计算下降发动机油门开度。

在舱内宇航员的眼里,DSKY 荧光显示屏猛地黑了一下,零点几秒后重新亮起,1202 警报依然在闪烁。

但阿姆斯特朗手里的操纵杆依然响应敏捷,下降发动机的轰鸣平稳连贯,飞船姿态没有哪怕半度的偏差。

而在地球上的休斯顿指挥中心里,24 岁的年轻工程师 Jack Garman 猛地趴在桌子上,死死盯着自己用透明胶带贴在桌角上的那张手写警报速查卡。

他认出了 1202 代码的本质。他立刻在耳机里向指挥席大吼:“这是 Executive 内存溢出!只要警报不是长亮不灭,说明系统正在自愈,不用中止,继续着陆!”

指令从后室传到飞控官 Steve Bales,传到飞行指挥官 Gene Kranz,再由通信官 Charlie Duke 传送上太空:

“鹰号,这里是休斯顿。警报放行,允许继续降落(We're GO on that alarm)。”

三分钟后,在推进剂只剩最后三十秒极限存量的时刻,阿姆斯特朗操纵登月舱掠过巨石,稳稳落在静海平原:“休斯顿,这里是静海基地。鹰号已经着陆。”

三万英尺边缘留下的工程启示

阿波罗 11 号之所以能创造历史,是因为系统设计者在半个世纪前就悟出了一个现代软件工程师依然经常忘却的硬道理:在极端过载面前,系统的故障行为是架构决定的,而不是听天由命。

在 Hamilton 与 MIT 团队最初主张在底层写入这套自愈与抢占逻辑时,NASA 内部甚至有老派官员认为这是画蛇添足,理由是宇航员都是精英,硬件不可能出错。

但如果 AGC 采用的是传统的单线程批处理循环,那场雷达中断风暴就会在几秒钟内把主线程彻底打死。计算机将错过点火计算的硬死线,发动机失去平衡偏转,阿波罗 11 号将要么坠毁在月球火山岩上,要么抱憾而逃。

Hal Laning 和 Margaret Hamilton 留下的这套架构,正是今天我们在现代分布式系统里奉为圭臬的 优雅降级(Graceful Degradation)与背压熔断(Backpressure & Circuit Breaking) 的鼻祖。

当资源枯竭时,优秀的系统从不试图做一个面面俱到的老好人。它会冷酷而果断地砍掉边缘功能——关掉屏幕刷新,丢掉雷达监控,把最后每一纳秒的算力留给那个唯一能决定生死的核心。

半个世纪前,在一堆用铜线手工织成的 64KB 内存里,这群年轻的程序员证明了一件事:当计算机无法完成所有任务时,决定它能不能活下来的关键在于它知不知道该放弃什么。