• 精选
  • 会员

04 令人费解的bug

2024年12月26日  来源:为什么需要生物学思维 作者:塞缪尔·阿贝斯曼 提供人:It8933......


bug并不都是能够找到确切起因的那些错误,在以连接和交互为特征的复杂系统中,经常会出现一些令人费解的bug。尽管我们对这些bug没什么好感,但它们却是这个纠缠时代中无法回避的存在。

20世纪80年代,游戏《小蜜蜂》(Galaga)非常受欢迎。这是一个经典的射击游戏,游戏者可以通过操控自己的飞船来射杀所有“敌人”。作为“古老”的游戏之一,它不仅画面单一,而且配音笨拙,然而这一切丝毫没有阻碍它的流行。不仅如此,它还有一个有趣的bug。如果你在进入游戏后立刻消灭了绝大多数的“敌人”,并在接下来的大约15分钟内避开了剩下的“敌人”,那么,这些“敌人”就再也无法对你开枪了。这是一件奇怪的事情,尽管游戏者常常利用它来取得高分。[1]

为什么会出现这样的情况呢?许多人认为,这是因为在某些特殊时刻,控制“射击”的代码出了故障,“忘记”了刷新。不过也有人说,这是开发者有意设置的一个功能,目的是便于开发者进入街机并获得高分。虽然后面这种猜测不无道理,但是我认为可能性不大,因为在其他游戏中,这种所谓的隐藏功能和作弊入口会“表现”得“干净”得多。这可能就是一个bug。

这个bug,以及其他无数类似的bug表明,我们并不完全了解自己所构建的系统。要想了解清楚在《小蜜蜂》这款游戏内部究竟发生了什么,为什么会出现问题,我们需要付出巨大的努力。尽管这款游戏的图形和规则看上去都很简单,但这并不代表它的复杂程度只是仅此而已;只有在它出现故障时,游戏者才能洞察到它的“真正实力”。在这里,bug不仅会指出需要修复的问题,还会告诉我们,我们正处于纠缠时代。[2]

早在1950年,艾伦·图灵(Alan Turing)就已指出,机器能够并且肯定会通过它们的行为,给人类带来重重“惊喜”。[3]现在看来,这种“惊喜”发生的频率将会不断加快。随着人类所构建的系统越来越复杂,系统的实际行为和预设行为之间的分歧也会越来越大。这种超出预期的行为,正是前文所述问题的具体表现。通过上一章的讨论,我们已经清楚,人类大脑与生俱来的若干局限性进一步推动了系统向复杂难解的方向发展。这就意味着,我们所有人,包括终端用户和业内专家,在看到火箭升空后很快自爆,或是精心制定的不同法律条文产生冲突时,都会倍感惊讶。错误和故障是系统复杂性的不良产物,是超预期的、不被接受的东西。

我们在上一章中引述过哲学家约翰·西蒙斯和杰克·霍纳的观点。他们对软件的开发过程进行了深入地研究,并重点关注了软件系统中那些通常不会被检查,以及不会被详细检查的方面。例如,在进行计算时,数据如何存储,怎样四舍五入,等等。这些最容易被掩饰、被忽略的方面,往往最容易滋生大量问题。在一个被广泛使用的引力模拟器中,人们发现,许多错误都与一种特定的数字处理方式有关。具体来说,在该程序中,一段只有3万行的代码,出现了大约1万个同类错误,而将这些错误修复之后,模拟结果便大为不同。[4]

一而再,再而三,我们发现,正是日益增强的复杂性导致了技术系统故障频出。当一种技术变得异常复杂之后,“狼人”就会出其不意地出现,并带来未知的影响。[5]在这些bug中,有一些错误是怪诞的,不过无伤大雅,譬如《小蜜蜂》游戏中的那个小bug;但是,也有很多错误是极具破坏性,甚至致命性的,例如“心脏出血”(Heartbleed)这种漏洞。这个错误主要出现在加密软件中,两年多以来,它一直威胁着全球2/3以上在线网站的安全,包括了Facebook和谷歌。

当然,说到bug,就不得不提到微软公司的Windows。1996年,Bug Detective出版了《Windows 95 Bug大全》(The Windows 95 Bug Collection)。这是一本专门讨论Windows 95的系统bug以及潜在解决方案的书。[6]这本书针对各种bug提出了一些解决方案,这些方案要比其他方案更容易管理。例如,Windows的某个版本在启动时会显示错误提示,而解决方案是:直接忽略提示信息。还有些bug的解决方案则需要更加激烈的干预手段。例如,如果你的计算机安装了某种专用控制卡,那么某些程序就可能无法在Windows 95系统中正常运行。那么,他们建议的解决方案是什么呢?答案是:“选择一台速度较慢的电脑,或者使用不同的柏努利控制卡(Bernoulli controller card)。”显然,这个问题并没有得到根本解决,或许,这个问题本就是无解的。

当系统庞大到一定程度时,上述情况就会发生。系统会以意想不到的方式与用户、其他系统,以及自身进行交互。事实上,在软件规模日益增长的同时,错误率也在大幅增加。不过,你不能就此认为,软件规模翻倍,每千行代码中的错误数量也只是会翻倍而已。[7]事实绝非如此。据估计,和拥有5 000行代码的程序相比,拥有1万行代码的程序在错误数量上是前者的4倍。

这种不可预测性和脆弱性,实际上是我们所构建的复杂系统的标志,虽然复杂系统对预料之内的冲击通常拥有令人难以置信的稳定性。这里所说的“预料之内”指的是系统拥有针对某种特定冲击的设定,但是在面对预料之外的冲击时,复杂性就会变成一种负担。

为了能更好地理解这种特殊情况,人们开发出了一个数学模型,即高度最优化容限(highly optimized tolerance)模型(11)。虽然系统在经过优化后可以适应各种各样的情况,但是任何“新异事物”都有可能让它们出现灾难性的故障,甚至崩溃。以波音777为例,[8]这种大型飞机是一台极其庞大的机器,包含了150 000多个子系统模块,所有模块指向的目标都是:确保正常飞行,并应对各种情况。但是很显然,它无法应对所有的意外情况。据业内专家称:“波音777在应对大规模的气流干扰、载重和燃料的变化、边界层的湍流流动,以及材料的老化与不均匀等情况时,具有很强的稳定性,但这种稳定性可能会因少数超大规模的集成芯片的细微变化,或者某些软件故障而失效,从而导致灾难。”[9]换句话说,随着系统变得越来越复杂,再细微的刺激都有可能引发灾难。其实我们根本不知道未来可能会发生什么。

事实上,这些意想不到的后果与边界情况和例外情况有关。世界很大很复杂,所以需要一个更大更复杂的系统来管理它。很多情况虽然都具有偶发性,非常罕见,但是却极有可能导致技术故障,因为总体数量实在太多,而且无法被一一测试。还是以丰田汽车为例,我们不可能对它的软件系统进行全面且彻底的测试。正如计算机科学家菲利普·库普曼所说:“常规的车辆检测根本不可能找出所有不寻常的故障。”[10]这就好比,一个人就算穷尽一生也不可能遍历所有可能会发生的事故。

在超越大脑极限的复杂世界粉墨登场之后,噩梦随之而来。当然,这场噩梦并不是指具有自我意识的天网(skynet)已向人类宣战,而是说系统已变得越来越复杂,越来越混乱,以致各种故障接踵而来,不管人们能否预料到。复杂性注定会带来意想不到的后果,而我们却只能在问题出现时才意识到。

如涉及版权,请著作权人与本网站联系,删除或支付费用事宜。

0000