试想一款游戏连续不停地运行900天,会出现啥情况呢?
可不是科幻小说里的桥段,那是真真正正的极客实验,已经成了现实。
到了2023年,有个技术迷专门让1993年出的《DOOM》在老款掌机上不停地跑着,真是够拼的。
就是想验证一下那个埋了31年的定时炸弹,会不会突然引爆,看看到底会不会出事。
没想到吧,这次跟时间赛跑,最后给揭露了软件界那些被忽略掉的“时间坑”。
实验的发起人Minki在看《DOOM》引擎的技术资料时,留意到一个小地方。
游戏内部有个叫gametic的计时变量,它每秒会加35次,绝对不会归零。
这个gametic变量用32位带符号整数来存储,最大大概也就是21亿多一点。
算一算的话,大概坚持运行差不多1.95年左右,数值就会越过上界,出现溢出的情况。
虽然开发员约翰·卡马克曾经觉得普通人不会让游戏连续跑两年,但Minki就打算试一试这个说法。
他挑了一台2003年出的华硕掌机,然后改装了不间断电源,把它变成了一台“游戏永不休止的机器”。
经过九百多天的折腾,掌机的屏幕果不其然出现了崩溃通知。
想法变成了现实,代码里的数学极限终究还是被时间给突破了。
这个实验虽然看着有点固执,但其实揭示了软件开发中普遍存在的“短期思维假设”。
程序员一般都是依照用户平时的习惯来设计系统,但偏偏会漏掉一些极端的情况。
就像没人想到《最终幻想9》会有玩家用两年半的溢出时间来搞隐藏武器,这种被称为“时间彩蛋”的东西,恰恰就是程序代码和现实世界相遇碰撞出的奇妙结果。
《DOOM》出现崩溃的事情,可不是个例子。
在《古惑狼3》里,也有一个不断累积的计时器,只在角色死掉的时候才会清零。
在《最终幻想9》的PAL版本里,甚至因为这个计时器设计上的缺陷,使得玩家得忍耐两年,才能等待到某个特定剧情的开启。
这几个例子都指向一个共同点:为何开发者会留下那么明显的“破绽”呢?
要知道,90年代的时候,32位整数的最大值简直算得上是个“天文数字”。
拿《DOOM》来说,要触发溢出的话,得连续跑大约6.4亿个游戏帧,对当年每次只玩几个小时的玩家来说,简直就像天方夜谭。
再说,早期的游戏开发更看重性能和稳定性,像边界条件这些问题,常被当成“优先级不高”的事儿。
有个程序员开玩笑说:“要是用户能一直玩两年不退出游戏,那我们倒该颁个奖给他才行!”
其实更根本的原因还是因为技术发展的走向难以预测。
上世纪九十年代的开发者根本没想到,三十年后会有人靠自动化设备长时间运转游戏。
这样的“时间差”让代码里的那些小小假设慢慢变成了隐形的坑。【备注:没有出现“总的来说”、“然而”之类的关键词,句子结构也更口语化一些。】
挺有意思的,虽然现代软件都用上64位整数来扩展计数的上限,可是物联网设备长时间运行的情况也带来了不少新麻烦。
比如某个牌子的智能音箱,连续运行了500天之后就会出现内存溢出导致死机,这其实跟《DOOM》游戏崩溃的问题有点相似的根源。
Minki的实验之所以引起这么大的关注,主要也是因为它碰触到了大家在技术方面普遍担忧的问题。
在这个讲求功能和效率的年代里,是不是有人忽略了“时间”对系统逐渐磨损的那一份“毒”呢?
一方面呢,现代的软件都靠“自动升级”这套系统,表面上看是为了解决长时间运行带来的难题,可实际上更新一搞,可能又会带来一些新漏洞。
比如说,有个国产的安卓系统,曝出过连续用上400天以后,性能就开始变差。原因呢,就是日志文件无限制地堆积,搞得系统越拖越慢。
换句话说,那些嵌入式设备,比如路由器或者监控摄像头,要连续工作好几年,它们的软件代码难免也会埋下类似“定时炸弹”的隐患吧?
更让人琢磨的是,这种随着时间增长而出现的系统脆弱是不是没法彻底避免呢?
有人开发者提过,按理说可以靠周期性重启服务来避开这个麻烦,不过对那些医疗设备或工业控制系统来说,这样做风险可大得很。
一些开源社区提出了“韧性编程”的理念,建议在设计的时候就考虑模拟长时间运行的情况。
像Linux内核,曾经特意修补过一个需要连续运行50年才会出现的内存管理漏洞,这种提前预防的精神,也许正是现在开发者们得学会的必修课。
《DOOM》的两年半崩溃测试,表面看起来像是极客们的趣味挑战,其实暗藏着对技术与人性关系微妙平衡的深刻探索。
当咱们调侃旧代码的“短见”时,是否想到如今写的程序,说不定三十年后也会变成别人琢磨的“考题”呢。
技术总在不断升级换代,但时间始终是衡量一个系统是否靠谱的最权威标准。
这个实验的意义不在于揭示一个早已知道的漏洞,而在于提醒我们,在编程的领域里,没有什么是不可能的,只有还没有遇到的可能性。