注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

My Unix World

不要迷恋Unix,Unix只是计算世界很小的一部分!

 
 
 

日志

 
 

转载:是否已落后于时代 我讨厌UNIX的十大理由  

2008-11-25 14:05:01|  分类: L-Unix |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

是否已落后于时代 我讨厌UNIX的十大理由
2005.12.15  来自:IT168 
    1971
年,《UNIX时代系统分享》第一版刚刚出版。这个简单的操作系统允许多个用户来使用一个低端的微型机,马上就变得非常受欢迎起来,很大程度上是因为资源代码可以被大学自由使用,这诞生了伴随着学习UNIX而成长的一代人。

  比起开发他们自己的操作系统,许多的公司被授权允许使用UNIX的源代码并制造出他们自己的派生系统来在他们自己的硬件上运行。最后,UNIX取代成为了最商业化的操作系统。

  在上个年代,UNIX的自由复制版本和派生版本开始接管保守派UNIX系统的地位。在源代码方面,这些版本已经很少使用了,如果有任何使用的话,那也是它们最早版本的故事了;但是在设计和原理方面,有很大程度上仍然能够追溯到最初的根基。

  UNIX有很多的长处,但是和其他的设计一样,它开始显现出跟不上时代的问题了。这篇文章里所列出的观点问题并不是所有UNIX系统都有的,有些会有多一些的问题,有的会少一些。

  1 所有的一切都必须是一个文件(除非它不是)

  所有的一切在UNIX系统里面都必须是一个文件。当然,除了那些不是文件的东西以外,比如说网络套接字。这被广泛看作是一个UNIX的定义之一。什么是在UNIX里的一个文件?就是一段字节。没有任何类型的信息是编码的,所以你想要理解一个文件内容的唯一方法就是已经知道它是关于什么的。

  文件的暗喻性在UNIX里面已经变得越来越明显了:

  物理磁盘是文件——这些文件有固定的大小,但是你能够在磁盘上寻找任何一个点;

  串行端口是文件——这些文件能够读写,但是寻找一个串行端口是没有意义的;

  一般的文件也同样是文件——并且这些文件能被连续和随机地读取,甚至是改变文件大小。当然,给了一个文件名的程序并不可能知道是个大概什么类型的操作在这个文件上,你只有去试一个操作,然后看看会不会失败。在好的UNIX惯例里,这样的一些操作,比如像一个NFS-shared文件,都显示的是正在工作,但实际上在后台就失败了。

  经常举的一个有UNIX优 点的例子是:你能够把程序和设备直接连接起来,并且它们可以工作,并不需要为设备界面来进行特别的设计。这在一方面来说是完全正确的。在一个只能进行线性 文本打印的打印机上的书写是和在一个文本终端机或是在一个文本文件上的书写是完全一样的。尽管如此,现在更多的人趋向于使用除了单一文本以外的一些东西。 如果我有一个程序能够输出一个图像,我能不能就直接把这个图像发送到终端机上并且让它显示出来呢?我能不能用同样的方法再把它发给打印机呢?当然,如果我 有一个X版本的服务器,并且能够支持X版本的打印机外设,那么答案会是可能的。但是如果我没有,那么我只能把它用X绘画调用的方式发到屏幕上,以PostScript语言(一种图解类程序设计语言)的形式发给打印机,然后以一个连续的字节流发给一个文件。当我不得不去弄懂另外一端的设备时,这些设备有同一个界面根本就不是一件好事——实际上对于我来说,这只是又多了一个更高的抽象层罢了。

    2 所有的一切都必须是一个文本

  在文件里的所有一切都是一个字节流。对于文本来说这没什么关系。当然,如果每个人都生活在美国并且永远都只需要使用7(比特)位的美国信息交换标准码,那么对于文本来说是没什么关系的。不幸的是,还有人生活在其他的地方或者是使用其他不同字符集的设备。所有的用不同代码的人们需要某些机制来解释他们所使用的字符集的意义。

  这个假设是:所有的一切应该都是文本,涵盖在UNIX设计中。除了在极少数几个案例中,指令是用来产生或期望产生文本的。与另外一个UNIX设计原则——把所有的一切都放到同一个命令行解释器(shell)里,即使它是属于一个共享库——来相比,这是一个更加奇怪的设计。一个聪明的方法应该是:命令产生标准的二进制数据,并且由那个命令行解释器来把它显示出来,如果它是准备被显示出来的话。

  我们来想想ls命令,这是一个能列出某个目录下内容的命令。如果你想要把这些内容以区分字母大小写为顺序整理排列出来,你需要把输出通过管道放到排序命令中来。现在想象一下你要把输出以文件的大小为顺序排列出来。你可以用ls来显示出这些文件的大小,然后让排序命令以文件大小行为顺序进行排序。这是可以的,但是接下来所有的文件大小都只是以字节来显示了(或者有些时候是以分配单元来显示,通常是512个字节为单位,取决于你UNIX的变量)。这种方式并不是那么友好,所以你让ls把所有的输出再换成友好的形式——用字节、千字节、兆字节等等来表示。不幸地是,排序命令并不懂得1MB是比6KB要大的,所以它就把所有的顺序都很蠢地弄乱了。或者是另一种方法:如果ls把输出作为一个有标题的定义,把它们以加上文件名字、类型的竖行显示出来,你让sort把这个竖行以文件大小来排列,最后你还需要让你的命令行解释器把这个大小翻译成为一个人类能懂的形式。

    3 没有智能修正(Introspection

  UNIX的命令行被看作是它最有价值的资产之一。我喜欢一个命令行的原理概念,我认为UNIX的命令行解释器会是最不可能让它完美工作的执行之一(但是我认为UNIX的命令行解释器并不能够完美实现这个理论)。

  任何使用过zsh的人都会对它所提供的那些难以置信的自动完成功能很熟悉。对于很多的程序来说,它能够自动完成命令行的选项。当你使用secure copy(安 全复制)程序时,它甚至能够在其他的机器上构建一个远程命令行解释器并自动命名文件名。但是当我们来观察它是怎样来工作的时候,可能会带给每一个人恶梦。 对每一个程序(或者每一个程序组,如果你真的是很幸运的话),都会建立一个解析器来扫描从程序中输出的帮助并创建那个自动完成清单。当你添加的新程序并没 有制造出一个帮助屏幕,或者是制造出一个不同格式的时候,zsh就卡住不能运行了。

   这样来考虑,现在在每一个目标,包括程序中,都有一个假设系统来支持简单的智能修正方法。在这个系统里,命令行解释器会询问那些有良好定义界面的应用软 件去获得一个可用选项的清单。它同样能够限制输入文件的类型和其他有用的数据。在一个理想的系统里,这个信息将会被放在很精确的位置,因此用户能够以他自 己的语言进入到命令行选项里。

  在很多情况下智能修正都是很有用的。在Mac OS操作系统上,潜在的文件系统支持在文件上最基本智能修正的形式——你能够获得一个32比特的文件类型。这并不理想,但是它至少允许做出某种意义上的智能决策,来决定使用哪一个应用软件来打开文件。把这个能力更提高一步来说,每个文件类型应该有一个文件结构上的机械部分的描述来和它一起。这样以后,一个文字处理程序就能够从另一个系统中扫描到一个文件,并且用这个信息把它很轻松地翻译成它原有的形式——至少,它能够提取出单一文本并且可能进行某种基础的格式化。

    4.     X11:几乎是一个图形用户界面

  X11是用来在屏幕上显示像素的一种机制。除此以外,它支持一些非常基础的窗口功能。如果你想要显示诸如按钮或是滚动条一类的东西,你需要一个更高层的工具包。

  对X11本身来说,这并不是一个坏主意。添加额外的抽象层只会导致轻微的性能降低,但是这使得编码需要更多的维护。首要的问题是我们并没有一个为X11配置的标准工具包——或者说,有那么几个,但是它们在应用的时候却是非常难做出与你需要的那些按钮或是滑行条一类东西相同的基础外表和感觉的。当然,缺少一系列关于人性化界面的指导丛书也是对X11没有什么帮助的。

  X11最主要的问题就是网络透明性。X11的设计要求之一就是它允许远程显示。不幸的事,这是在一个很低的层面上的远程显示。所有的X11确实是远程显示了——在连接中所有的另一端的逻辑冗余都显示了出来。如果你是在一个快速的本地连接中,对这个差别不会有太大感觉。如果你是在另一端慢速的或是高等待时间的连接中,你的感觉就会很不好受了。

  在X11变得很受欢迎的时候,另一个系统也被开发出来了。NeWS是在PostScrip语言的基础上开发出来的。对于开发者来说,开发这个系统的最明显的优势原因在于:在PostScrip的输出形式下,你能够给打印机发送一个与发送在屏幕上完全一样的输出。第二个优势为:它能够在连接到屏幕的机器上运行UI分量。如果你摁了一个按钮,足够的逻辑能够在本地为这个按钮的启动做出反应,并向服务器发送相应的活动。

  在X11模式上,按钮的点击被发送给机器去运行相关的应用程序,这要求必须把在动作序列中的每一帧都发送给屏幕。如果这个感觉不好,可以考虑以文本的方式再来感觉一下——每一次你输入一个字符,每个字符会被传输到服务器处理响应之后,再返回输出屏幕进行显示。现在来想象一下你所在的网络是以200ms(的等待时间连接在一个服务器上……

  所以开发NeWS的主意是很好的,以至于它一直不断的被改进。现在网络的最新专门术语“AJAX”是同一个概念下的另一个实现方式。浏览器处理用户界面响应,而后台服务器则异步处理应用逻辑。甚至在这个之前,Java applets程序就在做着同样的事情——Java中绘制用户界面和处理用户请求,与远程服务器交互而由其处理剩余应用逻辑。

    5 标准的输入,标准的输出

  在UNIX的命令行里,你能够使用管道来把程序连接起来。这对于简单工作流程来说是很好的。但它缺少灵活性,原因在于机制已经习惯于执行它了。最原先的是程序读出输入然后写出输出。在交互式的电脑上,拥有两条输出流会变得更加有益——一条是用来输出预期内容,另一条用来输出错误信息。如今在一个UNIX系统里,每一个单一的输入流都有两个输出流。

  这个限制意味着一些命令就不能被一个UNIX的命令行来执行。考虑一下一个过滤器把一个多元化的电影片段信号分离为视频流和音频流。最多你能够把视频流发送为标准的输出,把音频流发送为标准的错误输出,但是接下来你就没有办法来再把输出中的错误报告出来了。

  还有更糟的是,这些输出流都是单向性的。这就意味着两个过滤器之间是不可能互相协作的。在理想上的是,源过滤器应该能够发出不同输出格式的一个选择列表,然后目的过滤器能够根据这个列表选择一种适合的形式。但是在一个UNIX的命令行中,这个却需要由使用者来预先完成。一旦过滤器开始运行,就根本没有办法来改变输出的格式——例如在有很少网络带宽可以利用的情况下需要减小比特率。

6 同步系统调用

  在UNIX系统里,每一次你做系统调用,你不得不把它交换到内核空间里面去——一个相关的昂贵操作。你只能等待直到内核程序完成了操作后再继续下一步骤。在一些情况下,内核只是复制了你的参数然后留到以后再处理。考虑一下书写一些字节到一个字节流里面的情况吧。在UNIX系统里,你将要使用调用函数来系统调用。这将会切换到内核空间,拷贝字符缓冲,之后再进行处理。如果你幸运的话,不一会儿你就能得到一个关于书写成功(或是失败)的提示。

  如果你要写很多的数据,你不得不停止做一大堆的系统调入并花上很多时间来让你的CPU在特许模式和非特许模式之间转换。如果你是在一个SMP系统里,因为现在这种情况越来越普通了,你就必须等待直到所有种类的核心同步代码开始运行后才能做事。

  在一个例如QNX的微核系统里,系统的调入是不同步的。也就是说你能够让它们在一个用户空间缓冲器区里面等待,然后在你的CPU时间片后再把它们全部转换到核心空间里,核心程序再来打断正在进行的处理。这个方法让SMP系统升级了不少,并且提供了一个在单一处理器系统里以减少模式转换时间的需求来大力推进命令的执行。

    7 单行道系统调用

  虽然NetLink API解决了Linux里的部分问题,但是把数据提出核心却是很难的。没有任何的标准机制来让UNIX核心在一个UNIX程序里调入一个功能。事实上,从核心里要发送比1比特大的信息到用户空间程序里是非常困难的。那个1比特是一个信号、一个旗帜来说明有事请发生了。要举例子的话就是一个不同步的I/O请求。理想上来说,当你的I/O操作已经完成的时候你会得到一些提示。在UNIX里面,你会收到一个信号。这个信号告诉你一个不同步的I/O操作已经发生了,但是并不指出是哪一个操作——然后你就只有通过轮流询问所有的操作来找出到底是什么。

  缺少这个机制就很大程度上增加了核心内需要做的事情。这是一个UNIX的公理,那就是政策和机制必须是分开的。但是在UNIX核心内提供一个机制和在外部提供一个政策是很难的,因为核心并不能够通知用户空间代表什么样的政策决定是必需的。关于解决这个问题,标准的UNIX工作区的方式是让一个程序来发起一个系统调入,然后阻挡它,直到核心能够表达出什么,再在晚一些的时候把这个调入返回来。

  值得注意的是,这个并没有被应用到UNIX类的微核系统中来,例如GNU HURD或是Mach系统。这些系统使用一个非常简单的核心,并提供一个简单的界面给硬件和信息传递设备,然后运行所有的一切,但是就是除了在用户空间里。因此在这些系统里,核心代表了很多到用户空间的服务器,它需要一个机制来发送信息,并且这个机制要适用于所有用户空间的程序。

    8 C语言:跨平台PDP汇编程序

  C语言是用来帮助UNIX变得更轻便的。它是被设计来为PDP-11制造好的代码,并且非常近似于展现出UNIX机制的能力。在C语言里没有共发性的支持,比如说,在一个很现代的语言里,例如Erlang,原语存在于语言中是为了在它们中间制造不同的执行线和发送信息。在当买两台计算机要比买一台速度快两倍的计算机便宜得多的今天,这是非常重要的。

  C语言同样缺少一些在现代语言里应有的功能特性。其中最明显的一项就是缺少对信息串的支持。缺少对阵列的范围测试是另一个例子——这是要对UNIX软件中的大部分安全漏洞负责任的一个缺陷。其他为一些安全漏洞负责任的C语言的另一个方面是:C语言里面的整数有一个固定的大小——如果你想要储存某个太大的东西时,这个东西将会溢出。不幸地是,这个溢出并不那么好处理。在Smalltalk里面,溢出会被开发者很显而易见地发现并抓住,并且通过增加整数的大小就可以解决了。在其他的一些低级语言里,这个任务将产生一个错误并由程序来处理。在C语言里,它是被安静地忽视了。那么在C语言里无法改变的最小的整数是有多大呢?这个取决于整个的执行。

  接下来,我们该说说可悲的、不适当的C语言预处理程序了。C语言里的预处理程序是由非常简单的记号来替代的——它代码的底层结构并没有概念。这个步骤局限性的一个很好例子就是当你试图在这个语言上添加控制结构的时候。对Smalltalk来说这是微不足道的——Smalltalk里面的代码块能够像自变量一样被传递,所以许多信息的调入能够成为一个控制命题。在LISP里面,预处理程序能够被使用来编码设计模式,大大地减少了所需要的代码数量。C语言只能够处理内联功能类的相等物。

  但是C语言的真正问题在于:它是UNIX系统的标准语言。所有的系统调入和共用库都显露了C语言的功能,因为C语言是一个最小公分母——C语言实在是太低等了。C语言是在当程序上的范例刚刚获得接受时被设计出来的,当真正的程序员使用汇编语言和结构编程只是作为大学学术界所关心的事的时候设计出来的。如果你想在UNIX中做出一个面向对象的库,要么你就用它所用的语言暴露出来——强迫其他开发者选择和你所使用的一样的语言——要么你来为C语言写一个笨重的补丁。都不是什么好的解决方案啊!

     9 小的工具,但是并不小的库

  C语言的原理是用一个小的工具来做好一件事情。一个小的工具和一个小的库的区别是双重的:

  一个小的工具会有大一些的开销,它自己的处理需要所有相关的成本;

  一个小的工具灵活性会差一些。另一个程序利用它能够调用它(可能和一些命令行的自变量一起),然后它只能够被标准的输入所控制。这是非常不灵活的,并且会导致很多问题。

  最常引用的就是UNIXmv命令。UNIX的设计者们决定了设定命令行解释器,而不是应用程序,应该为展开的通配符做出反应。这就是说这个程序被调用了将没有办法知道是否自变量是原始的一个文件清单或是一个通配符。假设命令行显示为以下这样的:

  mv *.exe *.bin

  如果UNIX为通配符的展开提供了一个库,而不是为设定命令行解释器去做这件事,那么mv命令会知道你已经收到了两个自变量,并且你想要把所有扩展名为.exe的文件全都重新命名为扩展名为.bin的文件。但是在UNIX里面就不是这种情况了。那个mv命令接到了一个文件清单后会试图用在清单里的最后一个文件的名字来重新命名所有的文件。

    10 必须遵循的带内触发标准(In-Band Signaling

  任何一个通讯工程师都会告诉你带内触发是一个很糟糕的主意。但是在UNIX里面,它经常是唯一的一种做事方法。UNIX在元数据概念方面似乎有一个很大的原理缺陷。UNIX文件系统甚至不包括一个单一比特来表示一个文件是隐藏的。所代替的是,UNIX使用一个点(.)在一个文件名的前面来表示这是个隐藏文件。当Mac OS来使用32比特的文件类型代码时,DOS类的系统使用一个三字符的文件扩展名来定义文件的类型,而UNIX则把文件类型埋藏到了文件名里面。

  带内发信号载UNIX内是随处可见。所有的一切都是一个文件,所以所有的一切都是一个字节流。当其他的系统允许其他的元数据来和文件结合在一起时,UNIX只是把所有的一切作为长长的字节流。这导致了控制结构和数据被混合在一起,因为把应用程序连接在一起、或是把它和其他设备连接在一起的方式是一个假的文件。

  是U(NIX)退休的时候了

  UNIX在它的时代是一个非常好的操作系统。它在相关的低端微机上有很好的表现,并且提供了一些以前只能够在大型主机上才能运用的功能。对于一个PDP-11来说,它是一个完美的选择。现代的操作系统都基于同样的设计原理,但是却因为要把这个系统安装在一个比口袋计算器还要小的机器上所做出的妥协而受到了严重的威胁。
  评论这张
 
阅读(246)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017