The Kollected Kode Vicious

Kode Vicious - @kode_vicious

  Download PDF version of this article PDF

代码滥用

一个程序员的扩展是另一个程序员的滥用。


尊敬的 KV,

最近在工作中的一些停机时间里,我一直在清理一组库,删除死代码,更新文档块,并修复一些令人讨厌但并不关键的小 bug。这种代码探查揭示了一些库不仅被使用,而且还被滥用。每个人和他们的姐妹都将计时库用于他们能想到的任何事件,这倒不算太糟,因为它是一个旨在定期调用代码的库(尽管有些事件似乎根本不需要成为事件)。但当我意识到一些程序员正在使用我们的套接字类来存储字符串时——仅仅因为这些类碰巧附加了一些变量存储,而且其中一些在整个系统中是全局可见的——我差点吐了午饭。我们确实有可以轻松使用的字符串类,但这些程序员却滥用手头的任何东西。为什么?

被滥用的 API

尊敬的 Abused,

软件与现实世界不同的一种方式是,它更具可塑性——正如您刚刚发现的那样。虽然你可以用螺丝钉代替钉子,用锤子敲进去,但你很难用盘子当叉子。我们能够将软件变形为与原始作者的意图截然不同的形状,这既是福也是祸。

现在我知道您说过您清楚地记录了您编写的 API 的正确用法,但文档警告就像纽约乱穿马路者的黄色警戒线。除非他们和他们想去的地方之间有一条真正的火焰护城河,否则他们会走到那里,几乎不会停下来躲避警戒线。

给程序员一个钩子或一个 API,你就知道他们会滥用它。他们是聪明人,并且对自己有相当积极的看法,无论是否名副其实。被滥用最多的 API 是那些最通用的 API,例如那些用于分配和释放内存或对象的 API,特别是那些允许通过代码块任意管道传输数据的 API。

旨在以管道方式转换数据的系统简直就是在乞求被滥用,因为它们通常以非常通用的方式编写,以简单的构建块形式呈现给程序员。现在,您可能会说这些构建块是为网络代码、终端 I/O 或磁盘事务编写的;但无论您编写它们时的意图是什么,如果它们足够通用,并且您将它们留在其他程序员可以找到的黑暗地方,那么下次您查看它们时,它们可能已被以您无法识别的方式使用。更棒的是,当人们滥用您的代码,然后要求您使其按照他们想要的方式工作时。我喜欢那样,我真的喜欢——不,我不喜欢!!!

一个例子是各种 Unix 系统中硬件终端 I/O 的处理。终端 I/O 系统处理各种硬件终端固有的复杂性。对于那些太年轻而从未用过物理终端的人来说,它是一个连接到大型机或小型计算机的专用设备,允许您访问系统。它通常只是一个 12 英寸对角线屏幕,具有 80 个字符 x 24 行和一个键盘。没有窗口界面。诸如 xterm、kterm 和 Terminal 之类的终端程序只是硬件终端的软件实现,通常以数字设备公司 (Digital Equipment Corporation) 的 VT100 为蓝本。

在硬件终端流行的时代,每家制造商都会添加自己特殊的——有时是非常特殊的——控制序列,这些序列可用于访问诸如光标控制、反相视频和仅在一种特定型号上存在的其他模式等功能。为了理清各种终端供应商造成的混乱局面,BSD 和 System V 等主要 Unix 变体创建了终端处理子系统。这些子系统可以从终端获取原始输入,并通过引入理解终端实现差异的软件层,转换 I/O 数据,以便可以通用地编写程序,例如,将光标移动到屏幕的左上角。该操作将在用户当时正在使用的任何硬件上忠实地执行。

但在 System V 的情况下,同一个系统最终被用于实现 TCP/IP 协议栈。乍一看,这似乎是有道理的,因为毕竟,网络可以很容易地理解为一组模块,这些模块接收数据,以某种方式修改数据,然后将其传递到另一层以再次更改。您最终会得到一个用于以太网的模块,然后一个用于 IP 的模块,然后一个用于 TCP 的模块,然后您将数据交给用户。问题是终端很慢,而网络很快。当数据速率为 9600 bps 时,模块之间传递消息的开销并不显着;但当数据速率为 10 Mbps 或更高时,突然之间这种开销就变得非常重要。以这种方式在模块之间传递数据所涉及的开销是 System V STREAMS 今天鲜为人知或使用的原因之一。

当最终要拆除所有这些终端 I/O 处理框架时(即使有,也很少有硬件终端仍在服役),它们已被扩展来做的事情的数量才完全显现出来。有些事情是使用终端 I/O 系统实现的,几乎只是作为一种将数据输入和输出操作系统内核的方式,与任何形式的实际终端连接完全无关。

之所以这些系统如此容易被滥用,是因为它们被设计成易于扩展的,而一个程序员的扩展是另一个程序员的滥用。

KV

尊敬的 KV,

我的公司已经花了几个星期的时间来升级我们托管系统上的库。问题是,我们必须阻止所有用户在升级期间在这些系统上运行,这让他们很生气。几乎不可能解释升级需要是原子的。事实上,他们似乎不理解原子的原始含义。

即将核爆

尊敬的 Nuke,

是的,问任何程序员关于“原子操作”的问题,如果他们有任何线索,他们就会滔滔不绝地谈论测试和设置指令,甚至可能为您构建一个互斥锁或信号量。不幸的是,这种对如何保护数据结构或代码段免受同时访问的微观层面的理解并不总是能转化为宏观世界,在宏观世界中,必须完成一整套操作才能完成工作。由于某种原因,软件位之间的复杂连接以及代码模块如何相互关联逃脱了许多人——用户和程序员都是如此。

您和我都清楚,原子操作只是必须在一次事务中完成而没有干扰的某些工作。原子操作是根本无法进一步分解的操作。更新代码库的案例实际上是一个很好的例子。

代码位都具有相互依赖性。当一个库被更改时,所有依赖于该库的代码都必须更改以保持与相关库的兼容性。现代程序链接到数十个、数百个,有时甚至是数千个库。如果链接都是单向的——也就是说,程序仅连接到每个库——那已经够复杂的了。然而,在现实中,许多库需要其他库,依此类推,直到组合爆炸让我头疼——尽管实际上,我认为那是因为在试图理解上述互连之后喝了太多酒而宿醉了。

要更新系统中的单个库,您需要了解哪些其他库依赖于它,以及它们的 API 如何更改,以及这些库是否也是最新的。在这一点上,每个人都举手投降,只是升级了所有可见的东西,这使得原子操作的规模变得非常大。也许向您的用户解释这一点的最简单方法是让他们绘制各种代码位之间连接的图形,就像使用 Doxygen 等系统可以完成的那样。然后,当他们抓耳挠腮时,您可以关闭系统,升级它,然后重新启动它,在他们弄清楚图形之前很久。

KV

喜欢它,讨厌它?请告诉我们

[email protected]

KODE VICIOUS,凡人称之为 George V. Neville-Neil,为乐趣和利润从事网络和操作系统代码工作。他还教授各种与编程相关的课程。他的兴趣领域是代码探查、操作系统和重写您的错误代码(好吧,也许不是最后一个)。他获得了马萨诸塞州波士顿东北大学计算机科学学士学位,并且是 、Usenix Association 和 IEEE 的成员。他是一位狂热的自行车手和旅行家,目前居住在纽约市。

© 2012 1542-7730/11/1100 $10.00

acmqueue

最初发表于 Queue 第 10 卷,第 12 期
数字图书馆 中评论本文








© 保留所有权利。

© . All rights reserved.