MIT出品,发在1984年的TOCS上,很值得细细品味的一篇paper,可惜做presentation时由于是第一次在组会上讲,效果很差。

这篇paper的核心思想就是,在设计一个系统各个层次的功能时,如果把某个功能放到某一层时无法保证功能的完全可靠,那就干脆不要做,除非是为了性能等其他因素考虑。

这里举了一个文件传输的例子,假设要把电脑A的资料通过网络传输给电脑B,而其中文件系统、传输程序、网络等各个子系统都有可能出现问题,那么如果保证传输的可靠,即B能完整不出错的得到A的数据并保存呢?

如果把验证做在子系统,比如网络数据包校验,文件系统里也为每个文件加checksum,读出来的时候验证下,从而避免磁盘出错的可能。但是这些常见的措施只能保证部分环节的正确性,降低出错的可能,并不能从根本上保证数据传输的可靠性。举个例子来说,这里如果文件传输程序出了bug,网络和文件系统里面的检查即使通过了,最后的结果还是错误的。

针对这个问题的End-to-End的解决方案很简单,就是在电脑A上保留一份该文件的checksum,电脑B接收并保存后再算一次checksum,相等就说明传输成功了(当然这里要钻牛角尖的话也可以说还是有可能出问题的,但是这个概率已经小到可以完全忽视的地步了,在这里我们假设checksum符合就说明这个环节没有问题)。而这种保障措施不需要任何子系统的参与,也就是End-to-End的。

那么当一个子系统对要做的事情并不完全了解的时候(比如这里的网络传输层只能保证对方接受到的数据包的正确性,却不知道文件有没有损坏),是不是就没必要在子系统实现这个功能了呢?从正确性是来说是,但是考虑到其他方面,在子系统作检查还有另外两个好处:

一是大大的降低了出错的概率,准确的说是出错的概率呈指数级降低,这在并不需要一种完美的保障措施的场合下还是很有用的;

二是提高了性能,如果只用End-to-End的检验的话,有可能传输中丢了个包要等整个文件传输完毕做checksum的时候才会发现;而如果中间在网络传输层加个丢包检验的话就可以及早的发现错误并恢复了。这个问题在网络不可靠,或者文件很大的情况下尤为突出,仅仅是End-to-End的保证会使传输时间的期望值随着文件大小的增加呈指数级上升。

上网络课的时候有一次老师的问题就是既然OSI七层协议的上层(如transport层)已经做了校验措施,为什么下层(如data-link层)还会有“多余”的error detection呢?当时我想到的一个答案就是为了性能考虑,因为那会儿正好在读这篇paper ;-)

但是并不是说把功能放到子系统里就一定能提高性能了。如果子系统里面塞满了各种并不是上层都必需的检验机制,整个系统的性能势必会受到影响。所以这个trade-off值得深思熟虑。Exokernel的论文就提出了传统的操作系统中存在的这样一个问题,底层内核过于冗余,影响了应用程序性能,也隐藏了一些对部分应用程序比较重要的信息(比如给数据库提供磁盘原生数据访问的API可以获得更优的性能,另外应用程序也应当能控制自己发生缺页错误时候的行为)。

这里的“End”在不同的环境下会对应不同的模块。比如在网上聊天这个通信过程中,没有必要在底层做很多校验来保证数据包的完整性,这时候及时性更重要,当数据包丢失或者数据出错时,会有一个更常用的End-to-End的解决方案:没听清的那一方通过语音要求重述就行了,“我听不清,能再说一遍刚才的话吗?”,而此时的End自然是谈话双方。如果换一种情形,在语音留言系统中,就需要保证传输的正确性了,因为这时候数据的及时性变得很次要,而留言系统的特点要求用End-to-End的验证加上底层的措施来保证一方的语音信息尽可能忠实的保存到另一方的留言系统中。

修改于 2010.2.16