语句摘录:

Preface

知识和专能差异巨大,凭借知识可以推断该做什么,而专能可以让你在无意见,条件反射似的把事情做好。

少一些技术,多一些共享文化:显见和显微的,直观和潜流的,不止与方法,更重乎理念。

场景:哲学和历史;设计:哲学原理细分为有关设计和实现;工具:Unix提供的工具;社群:人与人之间的事物和约定。

须弥不重,芥子不轻;Unix程序员的教养。

构成文化是人,获知文化的方式可以是口口相传,潜移默化,也可以是书籍。

AdvancedProgramming in the Unix Environment[Stevens92]是探究UnixAPI的经典名著。ThePractice of Programming[Kernighan-Pike99]是每个C程序员必读的书籍。

从代码中汲取养分,将有助于将所学的原理消化为庖丁之技。

TheUnix Philosophy[Mike Gancarz],最简单的Unix设计手法就是持久耐用。

ThePragmatic Programmer[Hunt-Thomas]。《ZenFlesh,ZenBones》[Reps-Senzaki]-佛教禅宗本源的合集,禅为表达某种想法提供了丰富的词汇。

一些约定:

Unix手册页(manualpage),Unix程序簇(emacs).

Oldschool:昂贵的共用计算机,专属的Unix,shell脚本和无处不在的C

Newschool:脚本语言,用户界面,开放源代码的unix和万维网

PartI

Philosophy:PhilosophyMatters

文化,什么是文化?

工程和设计的每个分支都有自己的技术文化。在大多数工程领域中,一个专业人员的素养组成来说,不成文的行业素养具有与标准手册及教科书同等重要的地位(并且随着专业人员经验的日积月累,这些经验常常比书本更重要).资深工程师在工作中会积累大量的隐性的知识,通过类似禅宗“教外别传(不立文字,直至人心,见性成佛)”的方式,言传身教传授给后辈。

Unix演进成一种强势的技术文化,有鲜明特色的艺术和世代相传的设计哲学。

Unix的生命力

Unix被应用在各种机型上,从超级计算机到手持设备。Unix现已演化为:Linux,BSD,Solaris,MacOSX以及其他的各种Unix的变种。

Unix的核心技术:C语言—系统级软件工程,带目录节点的属性文件名字空间和吃呢工序间通信的管道机制。

性能-时间指数曲线对软件开发过程所引发的结果,就是每过18个月,就有一半的知识会过时。而Unix技术中很多的东西都是积年不变的:语言,系统调用,工具用法。

Unix的稳定和成功很大程度上归功于它与身据来的内在的优势,归功于早期的Unix看者作出的设计决策。这些设计决策和设计哲学,编程艺术,技术文化一起,被反复证明是健康可靠的。

Unix之失

Unix文件在字节层次上无结构可言,文件删除了就没法恢复了。安全模型过于原始,作业控制有欠精致,命令方式非常混乱。

Unix哲学中的最有争议部分是机制和策略分离的特性。这特性是由Xwindow设计者明确提出的,提供一套“机制,而不是策略”。Unix的其他系统级的服务也有类似的倾向:行为逻辑被最可能的推后到使用端。Unix可以在多种shell中进行选择,Unix应用程序提供了很多的行为选择和令人眼花缭乱的定制的功能。

上述行为体现了Unix的设计理念:最终用户比操作系统设计人员更清楚他们需要什么。

策略和机制分离的代价是:用户可以设置策略,其实上他们必须设置策略。只看眼前的话,Unix这种自由放纵主义风格会让它失去很多非技术型用户。长久的来看,策略是相对短寿,而机制才会长存。只提供机制不提供方法能使Unix长久保鲜。

Unix之得

开源软件:自由共享的源码和同僚严格复审的开发方式。

跨平台可移植性和开发标准:POSIX(读音更像Unix),只有称之为UnixAPI的等价物才算是比较可信的模型。

Internet和万维网:TCP/IP协议栈的开发合同交给Unix研发组,Unix成为互联网提供商(InternetService Provider)行业不可或缺的核心技术之一。Unix世界中可以看到稳定可靠的成果。

开源社区:Unix社区是各种软件的强大支持组,高质量的开源开发工具在Unix世界中极为丰富。借助这些工具,可以减少90%的工作量。通过协作开发与代码复用路上艰辛的探索,才耕耘出代码共享的传统。通过大量的工程实践,才有这些并非显而易见的设计原则。开源运行给Unix注入新的血液和新的技术方法,带来了新一代的有才华的程序员。Linux系统+Apache+Mozilla.

Unix的灵活性:Unix提供了众多程序粘和手段,将基本的工具箱的各种组件联合开合,将收到单个工具无法想象的功效。Unix将重点放在使各个程序接口相对小巧,简洁和正交—提高灵活性。

UnixHack的乐趣:趣味性很重要。乐趣是一个符号,意味着效能,效率和高产。

好的设计原则和开发方法在Unix上实施相对容易,Unix是一个学习这些原则和方法的平台,ANSIC程序库表述了相当打部分的Unix服务。

Unix哲学基础

Unix哲学注重实效,立足与丰富的经验,不会在正规的方法学和标准中找到它,它更接近于隐性的半本能的知识。它鼓励那种分清轻重缓急的感觉,以及怀疑一切的态度,并且鼓励你以幽默达观的态度对待这些。

Unix哲学的DougMcIlroy总结:一个程序只做一件事情,并做好。程序要能协作。程序要能处理文本流,因为这是最通用的接口。

Unix哲学的RobPike总结:

  • 无法断定程序会在什么地方耗费运行时间。瓶颈经常发生在意想不到的地方。

  • 估量(Little定律)

  • 花哨的算法在n很小的时候通常很慢,而且n通常很小。

  • 花哨的算法比简单的算法更容易出bug,更难实现。拿不准就穷举

  • 数据压倒一切。

Unix哲学的概述:

1.模块原则:使用简洁的接口拼合简单的部件

计算机编程的本质是控制复杂度。各种各样的软件方法将程序的复杂度提升了而不是简化了。

编写复杂的软件而不至于失败的唯一的方法就是降低复杂度--使用清晰的接口把若干个简单的模块组合成一个复杂软件。

2.清晰原则:清晰胜于技巧

代码注释,在选择算法和实现时考虑到将来的可扩展性。为了一点性能的提升而大幅提升技术的复杂性和晦涩性是不值得的。

3.组合原则:设计时考虑并接组合

在输入输出方面,Unix传统极力提倡采用简单,文本化,面向流,设备无关的格式。经典Unix下,多数程序尽可能采用简单过滤器(sed,awk)的形式,将简单文本输入流处理为一个简单的文本流的输出。

Unix中,文本流之于工具,如同面向对象中消息之于对象。文本流界面的简洁性加强了工具的封装性。程序要具有组合性,就要使得程序之间彼此的独立。

GUI可以是一个好东西,但是要避免使用复杂的二进制数据格式。在GUI中,考虑将复杂的交互程序和算法程序分离开,每个部分单独成为一块,使用简单的命令或者应用协议将其组合起来。文本流的数据可以使用通用的工具来处理。

4.分离原则:策略同机制分离,接口同引擎分离

X系统的设计,X成为了通用的图形引擎。分离变化的模式。机制和策略不是按相同的时间尺度变化的。

接口和引擎的剥离:比如将应用按照一个库开编写,库中包含使用脚本语言驱动的C服务程序,应用的控制流程使用脚本编写而不是使用c编写。典型的例子是Emacs编辑器。

另一种实践是,将应用程序分成可以协作的前端和后端进程,通过套接字使用上层的专用的应用协议进行通讯。

5.简洁原则:设计要简洁,复杂度能低就低

简洁为美,设法将复杂的系统分解为几个能够写作的小部分

6.吝啬原则:除非确无它法,不要编写庞大的程序

大的程序:体积大,复杂度高

7.透明性原则:设计要可见,以便审查和调试

调试通常会占用四分之三的甚至是更多的开发时间。透明性和显见性可以减少调试的工具量。

软件透明性:一眼就可以看出软件是做什么的。显见性就是程序带有监视和显示内部状态的功能。早点考虑测试或者调试事项,会给项目全过程带来好处。

程序如果要展示其正确性,应该使用足够简单的输入输出格式,这样可以方便的检验有效的输入和正确的输出之间的关系是否正确。

透明性和显见性的目的,提倡接口的简洁性。

8.健壮性原则:健壮性来源于透明和简洁

如果不能正确的理解一个程序的逻辑,谈何确定其是正确的!程序的健壮性要考虑极端大量的输入。程序越简洁,越透明,也就越健壮。

模块性(代码简朴,接口简洁)是组织程序以达到更简洁的目的的一个方法。

9.表示原则:把知识叠入数据以求逻辑质朴而健壮

数据要比编程逻辑更容易驾驭。在复杂的数据和复杂的代码中选择,宁可选择前者。更近一步,在设计中,应当主动将代码的复杂度转移到数据中去。

10.通俗原则:接口设计避免标新立异(最小惊奇原则)

最易用的程序就是用户需要学习新的东西最少的程序,或者最易用的程序是最切合用户已有的知识的程序。

关注目标受众。关注传统惯例,Unix世界形成了一套系统的惯例,比如匹配和运行控制文件的格式,命令行开关等等。

11.缄默原则:如果一个程序没有什么好说的,就沉默

沉默是金原则源自于Unix诞生的时候还没有视频显示器。简洁是Unix程序的核心风格。

设计良好的程序将用户的注意力视为有限的宝贵的资源,只有必要的时候才要求使用

12.补救原则:出现异常时,就马上退出并给出足够错误消息

异常处理逻辑,要尽可能的从容的应付各种错误输入和自身的运行错误。即使做不到,也要仅可能的以一种容易诊断的错误的方式终止。“宽容的收,谨慎的发的原则”

13.经济原则:宁花机器一分,不花程序员一秒

14.生成原则:避免手工hack,尽量编写程序去生成程序

人类很不擅长干辛苦的细节工作。程序中任何的手工的hacking都是滋生错误和延误的温床。程序规格越简单,设计者就越容易做对。程序生成代码总是比手写代码廉价并且更值得信赖。

编译器和解释器做的就是批量生成机器码的代码生成器。需要手写的重复而麻木的高级程序语言代码可以考虑使用代码生成器,并且代码生成器可以提高抽象度。备注:这里可以参考Paul关于Lisp的论断。

15.优化原则:优化前先要有原型,跑之前先学会走

做好原型设计可以让我们总是将时间用在刀刃上。

不知道瓶颈就匆匆进行优化,是唯一一个比乱添加功能更加损害设计的错误。过早的优化是万恶之源。考虑局部优化和全局优化的权衡。

先做原型,再精雕细琢。优化之前确保能用(KenThompson)

先运行,在求正确,最后求快(kentBack)。

使用shell脚本和awk代码构建原型。

16.多样原则:决不相信所谓的“不二法门”的断言

承认没有人能聪明的把所有的东西都最优化,也不可能预想到软件所有的用途.Beopen

Unix:采用多种语言,开发的可扩展系统,用户定制机制。

17.扩展原则:设计着眼未来,未来总是比预想来得快

绝不要认为自己找到了最终答案,要为数据格式和代码留下扩展空间。设计协议或是文件格式时,使其具有充分的自描述性以便可以扩展。要不包含进一个版本号,要不采用独立,自描述的语句。Unix经验:稍微增加一点让数据部署具有自描述性的开销,就可以在无需破坏整体的情况下进行扩展。

设计代码的时候,要有很好组织,让将来的开发者增加新的功能时,无需拆毁整个或者重建整个架构。

Unix哲学一言以蔽之:KISS(KeepIt Simple,Stupid!),这个有点像诗三百首,一言以蔽之,思无邪。

Unix哲学的应用

  • 只要可行,一切都应该做成来源和目标无关的过滤器(filter)。

  • 数据流应该尽可能的文本化(这样可以使用标准的工具来查看和过滤)

  • 数据库部署和应用协议应尽可能文本化(让人阅读和编辑)

  • 复杂的前端(用户界面)和后端应该泾渭分明

  • 如果可能,在用C编写前,先用解释性语言搭建原型

  • 当仅仅只用一门语言编程会提高程序复杂度时,混用语言编程才比单一的语言编程来的更好

  • 宽收严发(对接受的东西要包容,对输出的东西要严格)

  • 过滤时,重要的信息绝对不丢失

  • 小就是美,在确保完成任务的基础上,程序功能尽可能的少--大道致简

态度要紧

看到该做的就去做,这是捷径。如果不能确定是什么是对的,只做最少量的工作,确保任务完成就行了。

要良好的运行Unix哲学,必须要不断的追求卓越。软件设计是一门技艺,值得付出所有的智慧(wisdom),创造力(creation)和激情(passion)。

良好的运用Unix哲学,就应该珍惜你的时间决不浪费。不重复造轮子,不蛮干,好钢用在刀刃上。善用工具,尽可能将一切读自动化。

软件设计和实现应该是一门充满快乐的艺术,一种高水平的游戏。

History:ATale of Two Cultures

前事不忘,后事之师。Unix历史悠久,且丰富多彩。

Unix的起源及其历史,1969—1995

CTSS-->Multics-->Unix

创世纪,1969—1971

Unix诞生在KenThompson的头脑中,JohnMcCarthy(lisp)提出分时系统,实验阶段,性能不稳定。

早期分时系统的标准的交互设备是ASR-33点传打字机-->命令简洁,少说多做的来源。

PDP-7,文件系统,星际旅行。Multics交互式计算,良好的编程环境,伙伴关系的系统,Remote-access和分时系统的公共计算。Unix是第一个可以让程序员坐在机器旁,飞快的捕获灵感,并能一边编写一边测试的系统。激发饱受其他操作系统局限的程序员自愿为Unix开发工具。首个Unix应用程序,nroff(1)文本格式化程序,主旋律:文档格式化,排版,通讯工具。

出埃及记,1971—1980

最初的Unix使用汇编编写,应用程序使用汇编语言和解释型B语言编写。B+数据类型+结构=C。“性能的局限不仅成就了经济性,也且鼓励了设计的简约。”---穷则变,变则通,通则久。AT&T不能进入计算机相关的领域,

Unix反传统,嬉皮士和准嬉皮士,早期的编程者。许多大学都对Unix作出了贡献。

1979年Unix的v7版本,公认的版本,JohnLions V6版本的内核注释Unix内核的第一个正式的文档。

第一个Unix公司(theSanta Cruz Operation),商用C编译器(Whitesmiths),微软的XENIX。

TCP/IP和Unix内战:1980—1990

Berkeley,1974开始对Unix研究,1975-1976,kenThompson年休教学。BillJoy,1977,1 BSD. 1980,vi编辑器。1980,DARPA,Unix环境的TCP/IP协议栈。在1983年实现了TCP/IP的Berkeley4.2之前的Unix对网络的支持最弱。微软的雄起(个人计算机动摇工作站的根基),AT&T的拆分(Unix社区的内战,systemV vs BSD Unix)。

LarryWall(Perl)的patch程序可以促进Internet上的协作开发工作.

1985年Intel的386芯片,平面寻址4G的内存空间。RichardStallman的GNU宣言。Xwindows系统的无版权的源码发布,成为Unix厂商之间合作的中立地区,成为了Unix的图形引擎。IEEE支持的POSIX标准的发布,综合的BSD的信号处理和作业控制以及SVR3的终端控制。

1986年LarryWall开始开发Perl语言,PC技术脱离了IBM的控制。

Unix机器使用的Motorola的68000芯片。

OpenSoftware Foundation(IBM,DEC,HP等)vs AT&T/sun轴心。

反击帝国:1991—1995

1990年,WilliamJolitz的BSD移植到386机器上,386-BSD项目。Linux项目。1993—1994的互联网的大爆炸,Linux和开源BSD重要性开始为Unix社区中人们所知晓。Linux的开发方式依赖于分布式和patch工具,通过email和Usenet来招募开发者。ISP推动了互联网的发展,www是互联网中的杀手级应用。

互联网的推广增加了开发者的数量,降低了分布式开发的成本。XFree86形成了巨大的而有效的开发组织,为BSD和Linux添加了图形用户界面。1993年的Linux:Internet能力+X系统+GNU工具。

OSF+X/Open-->更大的Unix标准组

1995年之后,Unix的故事就成了开源运动的故事。

黑客的起源和历史:1961—1995

Unix传统是一种隐性的文化,传达这一个有关美和优秀的设计的价值体系,有自己的江湖和侠客。与Unix历史交织在一起的则是另一种隐性文化,“黑客文化”。1998年后,这种文化同“开源运行”重合了。

Unix传统,黑客文化以及开源运动间的关系微妙而复杂。三种隐性文化背后往往是同一群人,然而,其间的关系并未因此而简化。Unix身后的历史有一半是黑客的历史。

游戏在校园的林间:1961—1980

MIT购买了第一台PDP-1,PDP-1吸引了TechModel Railroad Club的成员的兴趣。TechModel Railroad Club中部分的人员成为MITAI实验室的核心的人员。1969,MITAI,Stanford,BoltBeranek&Newman(BNN),CMU(Carnegie-MellonUniversity)等连上了ARPANET。术语文件(JargonFile).

技术上来说,早期的黑客文化来源于PDP-11,使用汇编和Lisp方言编程。PDP-10的黑客接手了ARPANET,成立了IETF(InternetEngineering Task Force),开创了RFC标准化文档。

从社会性而言,早期的黑客年轻,天资过人,献身编程达到了痴迷的程度,决不墨守成规。计算机是构建社区的工具。

协作试开发和源码共享是Unix程序员的法宝。而对早期的ARPNET黑客而言,这是一种公众信仰,后来发展为关于网络思想社区的Chardinistidealism,RichardM StallMan苦行僧。

互联网大融合与自由软件运行:1981—1991

1983年后,BSD植入TCP/IP,Unix文化和ARPNET文化开始融合。木星项目的取消加速了两种文化的融合。1987年,文化融合完成,hacker用C编程,使用TMRC创造的行话。

RichardMStallMan和他的革命精神,最有能力的程序员之一,Emacs编辑器。1983,GNU项目,从黑客文化中创造出一种意识形态,将黑客社区变成一台有组织的社会化的机器,GeneralPublic License(GPL),魅力超凡颇具争议的文化英雄。

X开发者,庞大的分布式社群,电子邮件创意快速传递,网络改变了软件开发模式。

BSD开发者vsGNU项目,BSD时间和历史更长久,BSD比GPL更自由。

Linux和实用主义的应对:1991—1998

廉价PC机连上了互联网,GNU软件包解决了开发工具的问题,新生代的程序员的步入。意识服从经济。

Linus Torvalds利用GPL保护Linux内核,但不认同RMS协议的思想体系,不成为狂热分子。

Torvalds的实用主义,灵活而低调的行事风格,1993—1997年黑客文化的令人惊奇的胜利。技术上的成功和经济上的成功,Torvalds成为互联网时代的英雄。

1995年,Linux的杀手级应用,Apache服务器。

“如果有足够的眼睛关注,所有的bug都无处藏身”

“用自由软件是因为它运行得更好”打败了“用自由软件是因为所有的软件都应该是自由的”

大教堂与集市的开发模式的对比。

开源运动:1998年及之后

1998年时,黑客社区:FreeSoftware Movement,Linux社区,Perl社区,Apache社区,BSD社区,X开发者,IEIF以及其他成打的组织。

部落的凝聚力可以来自:维护的代码库,有影响力的领导者,语言,开发工具,特定的软件许可,或者是技术标准。

1995年之后,Linux,特殊角色,社区软件的统一平台,最佳品牌,兼并其他部落的倾向。黑客文化开始凝聚在一个共同目标:推动Linux和市集开发模式的发展。

Mozilla源码公布,1998.3社团重要领导人峰会,为所有的派系的共同开发方式确立了新标记—开源。六个月内,所有的部落都接受了“开源”的新标记。统一词令的作用是非同凡想的,提高了社区的作战的能力,避免内战,一致对外(windows)。

开源运动中,黑客社区迎来了各个领域(Java阵营,各类过时的操作系统的开发者)的移民潮。

Richard StallMan和FreeSoftware Movement和开源化清界限。

Unix的历史教训

Unix离开源越近就越繁荣,任何专有化都会使其停滞和衰败。

保持软件及其设计传统的灵活性才是长存之道。--问渠那得清如许,唯有源头活水来。

Contrasts:Comparing the UnixPhilosophy with Others

操作系统的设计,造就了该系统下软件开发的风格。Unix的编程哲学。

操作系统的风格元素

操作系统的设计和编程风格的三个源头:1)操作系统设计者的意图;2)成本和编程环境的限制对设计的均衡的影响;3)文化的随机漂移。

操作系统的统一性理念:Unix一切为文件模型以及在此基础上的管道概念。

多任务能力:DOS和CP/M就是一个顺序的程序加载器-->协作式多任务(cooperativemultitasking)-->抢占式多任务(preemptivemultitasking).

注意:多任务和多用户不是一个概念。多用户需要使用权限域来管理。

协作进程:低价的进程生成和简便的进程间通信(IPCInter-Process Communication)使得众多小工具,管道和过滤器组成一个均衡系统。

进程是自主运算单元的统一标记号,进程控制是可编程的---管道实现概念基础。

如果进程生成代价昂贵,且或进程控制困难,不灵活,后果是:

  • 编写巨大的单个程序成为更自然的方式

  • 策略必须在庞大的程序中表述。内部层级复杂,不如C代码的平坦的内部层次

  • 进程间通信使用低效的不安全的机制。

  • 广泛使用多线程来完成某些任务,而这些任务在Unix中要使用进程间协作即可完成

  • 必须学习和使用异步I/O

pipe和UnixIPC要求将进程间通信简化到某个程序从而促使功能的分解。

一个操作系统如果没有灵活的IPC和使用IPC的强大的传统,程序就得通过共享结构复杂的数据实现通讯。

内部边界:Unix的准则是程序员最清楚一切。Unix的内部界限的三个层次:1.内存管理——Unix使用硬件自身的MMU保证各自进程不会侵入其他进程的内存地址空间。2.为多用户设置真正的权限组—普通用户进程未经允许,不能修改或者读取其他用户的文件。3.将涉及安全性的功能限制在尽可能小的可信代码块上。即使是Shell也不是特权程序。

操作系统内部边界的稳定性不仅仅是一个设计的抽象问题,它对系统安全性有着重要的影响。

文件属性和记录结构:Unix文件既没有记录结构(recordstructure)也没有文件属性。操作系统级的记录结构通常只是一个优化手段,几乎只会使API和程序员的生活更加的复杂化。

二进制文件格式Vs纯文本格式

首选用户界面的风格:命令行界面(CLI)和图形界面(GUI).操作系统的设计者将那种哪种选作为表现模式,将影响设计的许多方面---从进程调度,内存管理以及应用程序编程接口。

如果操作系统CLI功能很弱或者根本不存在,其后果是:

  • 程序设计不会考虑未预料的方式相互协作

  • 远程系统管理更难实现,更难使用,更强调网络

  • 简单的非交互程序会招致GUI开销或复杂的脚本接口

  • 服务器,守护程序和后台进程几乎无法写出,至少很难以优雅的方式写出

  • 不具有脚本变成能力

目标受众:客户端/服务器之分。

开发的门坎:从纯用户-->开发者的门坎的高度。两个动因:1.开发工具的金钱成本2.成为熟练开发者的时间。

昂贵的开发工具+复杂晦涩的API--->小群的精英编程文化--->大型而严肃的程序

廉价工具+简单的接口--->轻松编程,玩家文化和开拓探索--->小程序和自我增强,不断扩展的知识社区--->长寿的操作系统

操作系统的对比

VMS: DEC(Digital EquipmentCorporation),完全抢占式多任务处理能力,进程生产代价高,非常全面的在线帮助—应用程序和命令选项,内部边界系统极佳,工具最初昂贵,界面复杂。

MacOS

设计变化:1)单个程序到多个程序(MultiFinder);2)68000-->PowerPC3)Mac Os设计理念+Unix架构-->Mac OS X.

Mac界面方针,应用引擎和GUI接口相对清晰的分离开来。数据分支(datafork,文档和代码),资源分支(resourcefork,用户定义的文件属性)

OS/2:抢占式多任务处理,需要MMU,进程生成成本低,IPC困难,不支持多用户,WPS(Workplaceshell,干净,强大,面向对象的设计,易懂的行为特征和良好的扩展性,成为GNOME工程的模型),文本模式和二进制模式,

WIN NT(NewTechnology):逐步堆积而成,进程生成昂贵,广泛使用二进制文件,CLI支持力度弱,脚本能力弱,Unix的系统配置在以点(.)开头的文件,NT集中在注册表中-->系统不具备正交性,单点故障毁坏注册表;registrycreep。

访问控制机制不闲置,内部边界漏洞多,程序库版本控制问题(DLLhell),Cygwin。

早期第三方开发商提供应用软件,后来内部开发战略。专业级编程和玩家文化以及轻松开发文化所支持共享软件设计风格的分歧。

BeOS:结合Unix和MacOS的失败,立足多媒体平台,统一理念-深入的线程化,多媒体流,数据库形式的文件系统。IPC通过共享内存实现,Unix的内部边界的设计,利用MMU,二进制和文件系统,GUI和CLI共存。经营战略不精明。

MVS:IBM大型计算机的旗舰操作系统,极高的OS/360兼容性。一些皆批处理,有效的利用机器处理巨大规模的数据,尽量减少与人的交互。JCL控制语言,通用的安全性API,社区存在繁荣的自由软件文化(编程和系统管理工具)

VM/CMS:Unix的伯父,VM/CMS系统的统一概念是虚拟机,由VM提供。支持提供成千上万的用户的伸缩性。使用的汇编进行编程,没有像C语言那样的工具,IBM使用PL/I执行系统编程。

Linux:1991年LinusTorvalds发明,是1990年后出现的新学派开源Unix阵营的领头羊(FreeBSD,NetBSD,OpenBSD,Darwin).一方面:Linux社区的目标是桌面用户,一方面:Linux用户和开发者不断通过自我调整来消弥非技术用户对CLI的恐惧。

贴近终端用户的愿望使得Linux开发者更加关注系统安装的平稳性和软件发布的问题。Linux支持其他操作系统特有的文件系统格式的读写以及联网的方式。Linux开发者广泛的吸收了非Unix操作系统的设计理念,强调使用尽量少的资源做尽可能多的事,不会为了在高端硬件上获取最大性能的效益而增加低端机器上的复杂度。

Unix竞争者的劣势

不可移植性,不具备良好的网络支持能力,多用户能力弱,多任务能力差,IPC能力差。网络使得服务器操作系统和客户端操作系统重新汇合到一起。将GUI应用到服务器操作系统比将服务器的特性(多用户优先权和完全多任务处理)改装到客户端操作系统中要容易。

Part IIDesign

Modularity: Keeping It Clean,KeepingIt Simple.

There are two ways of constructinga software design . One is to make it so simple that there areobviously no deficiencies; the other is to make it so complicatedthat there are no obvious deficiencies. The first method is far moredifficult.

代码划分的方法有一个自然的层次体系,并且随着程序员必须面对的复杂度日益增加,这个体系也在演变。大块机器码-->子程序划分代码-->服务程序库,程序共享公共函数-->独立地址空间和可以相互通信的进程--分布式的程序系统。

自19世纪晚期,人们使用标准螺纹以来,硬件的模块化就一直是工程技术的基石。同样,模块化对与软件来说也是同样的重要的。

Unix程序员传统是:笃信并重视模块化,更注重正交性和紧凑性等问题。

早期的Unix程序员在很多的方面作出了努力,尽管函数调用会消费很多的时间。但是模块化控制了复杂度。设计模式运动希望更近一步,找到成功的设计抽象原则,以此组织大规模程序的结构。

封装和最佳模块大小

模块化的代码首要的特质就是封装。封装良好的模块不会过多的向外部透露自身的细节,不会直接调用其他模块的实现码,不会胡乱共享全局数据。模块之间通过应用程序编程接口(API)------一组严密,定义良好的程序调用和数据结构来通信。

API在模块间扮演双重角色。在实现层面,作为模块之间的chokepoint,阻止各自的内部细节被相邻模块知晓。设计层面,正是API定义了整个体系。

验证API是否设计良好的方法:尝试使用纯人类的语言描述设计(不许摘录任何的源代码),检测是否可以将事情说清楚。养成在API编写之前编写一段非正式书面的描述。写代码的流程:定义API,编写简要的注释—对其描述,编写代码。

软件系统应该由层次分明的嵌套模块组成。

缺陷的密度和模块的大小的成U型的曲线关系。Hatton的经验数据表明,假设其他的能令相同,200-400之间的逻辑行代码是“最佳点”。

紧凑性和正交性

语言和API同样也受HattonU型曲线影响。

在设计API,命令集,协议以及其他让计算机工作的方法时,需要考虑紧凑性和正交性。

紧凑性:一个设计能否装进人脑中的特性。测试方法:有经验的用户需要操作手册吗?如果不需要,这个设计就是紧凑的。

紧凑的工具同顺手的工具具有同样的特点:让人乐于使用,使工作更有效。

设计构建在易于理解且利于组合的抽象概念上,则系统可以在具备强大的,灵活的功能的同时保持紧凑。紧凑的设计聚集在一个内在的基础概念模型之前,在理解这个概念模型之前,要理解这个设计还是很困难的。

Unix系统调用API是半紧凑的,程序员很容易记住大多数的应用编程的系统调用子集(文件系统,信号,进程控制)。

《TheMagical number seven,Plus or Minus Two: Some Limits on Our Capacityfor Processing Information》--记忆启示。

make是紧凑的,HTML是半紧凑的,C和Python是半紧凑的。

有时为了性能和其他的因素,需要牺牲紧凑性。BSD套接字的API。

正交性:有助于复杂的设计变得紧凑的最重要的特性之一。纯粹的正交设计中,任何操作均无副作用:每个动作(API调用,宏调用,还是语言运算)只改变一件事情。

《ThePragmaticProgrammer》:正交性缩短了测试和开发的时间,不产生副作用也不依赖其他代码副作用的代码校验以来比较的容易,组合测试少,代码容易文档化和复用。

重构(refactoring)--ExtremeProgramming—提高程序的正交性。

Unix的基本API设计虽不完美,但是颇为成功。BSD的套接字API,Xwindow系统的绘图库,这些后加的修改都不正交。

SPOT原则:不要重复自身,真理的单点性---任何一个知识点在系统内部都应当有一个唯一,明确,权威的表述。

通过重构修改代码的组织从而去除重复的代码,

  • 如果代码中含有重复数据是因为在两个不同的地方必须使用连个不同的表现形式,能否写个函数,工具或者代码生成程序,让其中一个由另一个生成,或者两者来自同一个源?

  • 如果文档重复了代码中知识点,能否从部分代码中生成部分文档,或者让两者来自同一个更高级的表现形式?

  • 如果头文件和接口声明重复了实现代码中知识,考虑从代码中生成头文件和接口声明?

数据结构中也存在SPOT:”无垃圾,无混淆”(Nojunk,no confusion).无垃圾:模型最小化;无混淆:模型状态和真实系统状态一一对应。

SPOT原则在各种形式的代码生成器(编译器和解释器也是代码生成器)中充分体现。

紧凑性和强单一中心:围绕“解决一个定义明确的问题”的强核心算法组织设计,避免臆断和捏造,是提高设计的紧凑性的方法。

任务的核心问题形式化并建立这个任务的明确模型。很多的Unix工具都是围绕着某个单一强大的算法直接转换一个瘦包装(winwrapper)。典型的实例是:diff,grep,yacc和lex。

与形式法对应的是试探法,使用试探法的原因是所有已知的形式上的正确的方法开销都代价高昂.试探法会增生大量的特例和边界情况。

分离的价值:Unix核心概念清瘦如禅,限制,优雅的设计,尽量去想语言和操作系统最少能做什么事情,从零开始,紧凑正交的设计,依附导致痛苦,初心(beginner'smind),虚心(emptymind),抽象,简化,归纳。

软件是多层的

一般而言,设计函数或者对象的层次组织可以选择两个方向(软硬)。选什么,什么时候选影响着代码的分层。

自顶向下和自低向上:从具体到抽象,从抽象到具体。Top-down:先考虑主循环,然后是主循环调用的操作的服务代码库。Bottom-top:封装具体的任务,按某种次序吧这些东西粘和在一起。

以自顶向下的应用逻辑表达抽象规范,以函数或者库来收集底层的域原语。

Unix程序员继承了居于操作系统设计核心的传统,更适合使用自底向上的方法。

实际的项目往往是自定向下和自底向上综合产物,同一个项目中同时兼有自顶向下和自底向下的代码,就导致了胶合层的存在。

胶合层:顶层的应用逻辑和底层的域原语集必须使用胶和逻辑俩进行阻抗匹配(impedancematch)。

教训:胶合层是一个讨厌的东西,必须尽可能的薄。胶合层是用来就爱那个东西粘在一起的,而不是用来隐藏各层的裂痕和不平整。胶合层bug丛生。

薄胶合层原则分离原则的升华,策略(应用逻辑)应该和机制(域原语)必须清晰地分离。

被视为薄胶合层的C语言

《ComputerArchitecture:Concepts andEvolution》指出从早期的大型机到小型机,工作站再到PC都在趋同同一种形式(经典形式):二进制表示,平面地址空间,内存和运行时存储(寄存器)的区分,通用寄存器,定长字节的地址解析,双地址指令,高位字节优先以及大小一致为4位的整数。

C语言是一种结构汇编程序,可以为理想化的处理器和存储体系服务,非常的适合于微处理器。C语言是处于经典体系之上的一个薄而灵活的胶合层,清晰,简洁的最简化设计,强大的生命力。

完美之道不在于无可增加,而在与无可删减。--《白鹭》中“增之一分由嫌长,减之一分则嫌短,素之一忽则嫌白,黛之一忽则嫌黑”可以说是一种对美的描述。

程序库

Unix编程风格强调模块和定义良好的API-->强烈倾向于把程序分解成有胶合层连接的库集合,特别是共享库(window下称为DLL)。策略和机制分离的软件设计方法,《TheDiscipline and Method Architecture for ReusableLibraries》,Unix世界里,作为“程序库”发布的库必须携带练习程序(exerciseprogram,优秀的测试框架),库分层的一个重要形式是插件。

实例分析:GIMP插件

GIMP被做成一个图像处理和辅助程序库,有一个相对比较薄的控制代码调用。驱动代码知道GUI,不直接知道图像格式;程序库程序知道图像格式和图像操作,不知道GUI。libgimp已经被文档化了,可供其他程序使用。C代码写成的插件可以由GIMP动态的载入,让后进行图像处理。

Unix和面向对象语言

OO设计理念最初在图形系统,图形用户界面和某些仿真程序中被认可,其他的领域没有显著的优势。

Unix的模块化的传统和围绕OO语言发展起来的使用模式存在一些对立的关系。OO通常被过分的推崇为解决复杂行问题的唯一的正确方法。Unix的传统的薄胶和层原则和OO提倡的厚重胶和层和复杂层次体系的区别。

OO的缺点:使得程序员陷入过度分层陷阱的倾向;简洁,清晰,透明的原则被破坏;程序的优化机会的损失。

Unix风格的程序面临的原则是如何将分离法的优点同代码和设计的薄胶和,浅平透层次结构的优点结合。

模块式编码

  • 全局变量的数目?全局变量是毒药

  • 单个模块是否在Hatton的最佳范围内?200-400逻辑行

  • 模块的单个函数是不是太大了?

  • 代码是不是有内部API---可做为单元向其他人描述的函数调用和数据集,且每个单元读封装了某个层次的函数

  • API入口点是不是超过7个?类的方法是不是超过7个?数据结构成员是不是超过7个?

  • 项目中模块的入口点数量如何分布?

Textuality :Good Protocols Make Good Practice

Unix传统所教导的两种不同而又紧密相关的设计:设计将应用数据存储在永久存储器中的文件格式和在协作程序中(bynetwork)传递数据和命令的应用协议。这两种设计的共通之处:内存数据结构序列化有关。

复杂数据结构最简单的表达是字段使用机器自带的数据格式(整数的二进制补码)表示,所有的指针都市实际的存储地址(相当于名称引用)。这种表达方式不适合数据的存储和传输:数据格式存在机器差异。

数据结构的序列化(也称为列集-marshaling),反序列化(也称散集-unmarshaling)。C/C++中需要进行列集和散集操作的代码占维护的代码中很大的比例。Python和Java内置了列集和散集函数,可以应用于任何对象或者对象代表的字节流。

互用性,透明性,可扩展性和存储/事物处理的经济性是设计文件格式和应用协议需要考虑的方面。

注意区分数据文件(由应用程序产生,大小不限)和控制文件(设置Unix程序的启动的选项,手工编辑,体积小).

文本流的重要性

文本流是重要的通用的格式。透明,简洁。担心性能问题可以在某个层次添加一个文件压缩的协议流。文本协议和二进制协议。

二进制协议正当理由:处理大批量的数据集-->在存储介质上取得最大位密度,减少数据转换的时间和指令的开销-->大图像,多媒体数据

文件格式的实例:Unix口令文件,.newsrc,PNG(PortableNetwork Graphics):位图图形的一种文件格式

数据文件元格式

数据文件元格式是一套句法和词法约定。Unix已经形成或者采纳了适合多种应用程序的不同元格式。尽可能的使用这些格式是一个好习惯。好处如下:1.可以直接使用现存的服务库2.减少开发者和用户的学习成本。

常用的格式如下:

DSV(Delimiter-SeparatedValues)风格:使用‘:’分隔,使用'\'转义字符

RFC 822格式:源自互联网电子邮件信息采用的文本格式。

Cookie-Jar格式:使用%%这种格式

Record-Jar格式:

XML:语法类似与HTML,支持低级语法,需要文档定义和相关的应用逻辑赋予其语义,适合复杂数据格式。优势:无需知道数据语义,仅仅靠语法检查就能发现错误;文档文件的标记格式。缺点:无法很好的和传统的Unix工具软件协作困难,需要庞大的XML解析器。

Windows INI格式:

[XXXX]

xxx = xxx

…..

xxx = xxx

Unix文件格式的约定

  • 如果可能,以新行符结束的每一行只存一个记录

  • 如果可能,每行不操作80个字符

  • 使用‘#’引入注释

  • 支持反斜杠约定

  • 在每行记录的格式中,使用冒号或者任何连续的空白作为字段的分隔符

  • 不过分区分tab和whitespace

  • 优先选用16进制而不是8进制

  • 对于复杂的记录格式,使用“节格式”:记录若有多行,就使用%%\n或者%\n作为记录的风格符

  • 在节格式中,要么一行一个记录,要不使用RFC822电子邮件头类似,

  • 在节格式中,支持连续行

  • 要不包含一个版本号,要不将格式设计成相互独立的自描述字节块

  • 注意浮点数取整的问题

  • 文件压缩时使用表现形式和具体的压缩的方法分离的策略

应用协议设计

复杂程序划分成几个协作进程,通过应用程序专用命令集或者协议通信的优点。

服务器进程使用统一控制程序控制,方式是服务器程序从标准输入中接受命令,然后将响应发送到标准输出。CLI服务器命令集,文本格式,简洁性。End-to-end设计原则《End-to-endArguments in System Design》。获得良好性能而设计的应用协议。SMTP,POP3,IMAP

应用协议元格式:为了简化网络间事物的序列化操作而发展过来的。尽管网络带宽比存储昂贵的多,需要注意事务处理的经济性,但文本格式的透明性和互用性优势依然十分的显著,可读性>性能优化.

《Onthe Design of Application Protocols》--经典的互联网协议描述手段。

通用应用协议HTTP协议

Transparency: Let There Be Light

Beauty is more important in computing thananywhere else in technology because software is so complicated.Beautyis the ultimate defense agaist complexity. --《MachineBeauty:Elegance and the Heart of Technology》

透明性:实际上能预测程序行为的全部和大部分情况,并能建立起简单的心理模型,软件就是透明的。

透明性和可见性对用户和软件开发人员都很重要。

透明性是美的组成部分之一,优雅的代码,优雅的算法,见解和信心,容易修改和维护。

Linux内核:透明而不具有可显性。EmacsLisp库可显而不透明。

实例分析:GCC

GCC是由一些的处理阶段(预处理器,解析器,代码生成器,汇编器,链接器)组成,并由一个驱动程序将其紧密结合起来。驱动程序具有监控开关。

透明之禅:追求代码的透明,最有效的方法最简单,不要在具体操作的代码上叠放太多的抽象层。

禅的教导:通常我们通过源于欲望的偏见和成见的迷雾观察世界,因此需要“去欲望,少依恋,如实见”。使用设计简单而透明的算法和数据结构贴近基础。

透明行和可显性而编程:

  • 程序调用层次中最大的静态深度是什么?不要大于4.

  • 代码是否具有强大,明显的不变性质?

  • API中的各个函数调用是否正交?或者存在太多的特征标志(magicflags)和模式位,使得一个调用要完成多个任务?

  • 是否存在一个顺手可用的关键数据结构或者全局唯一记录器(socreboard),捕获西欧他那个的高层的状态?

  • 程序的数据结构和代表的外部实体之间是否存在清晰的一一对应的映射关系?

  • 是否容易找到给定函数的代码的部分?不仅单个函数,模块,还有整个代码,需要花多少时间读懂?

  • 代码增加了特殊情况还是避免了特殊情况?

  • 代码中有多少个magicnumber?通过审查是否可以查实实现代码中的限制?

透明性和避免过度保护

良好的程序通常存在调试和探测开关(-verbose选项)。Unix传统大力倡导具有灵活性的程序,适应更广的使用范围和排除情况,包括用户愿意处理的时,有能力给用户提供尽可能多的状态和活动信息。

透明性和可编辑的表现形式:将不易实现透明性的定义域转换为容易实现的透明性的定义域问题。无论何时涉及编辑一个复杂的二进制的对象的设计问题,优先考虑编写一个文本化的编辑器。

编写文本化器或者浏览器的理由:

  • 获得良好的学习机会

  • 有能力将结构内容转储,以供审查和调试

  • 有能力轻松生成测试负载和特例

  • 可以获得可复用的代码

透明性,故障诊断和故障恢复

为可维护性而设计:可维护性原则要求代码遵循清晰原则-选择简单的算法,需要包含开发者手册。

Multiprogramming:Separating Processing to Separate Function

Minilanguages:Finding a Notation That Sings

A good notation has a subtlety(精妙)and suggestiveness which at times makes it almost seem like a liveteacher. --《TheWorld of Mathematics》

软件错误模式的研究的结论:程序员每百行出错的率和所用的编程语言在很大程度上无关。更高级的语言可以用更少的行数完成更多的任务,意味着更少的bug。

Unix的传统,包容小型的,为专门应用领域特制的,大量减少程序行数的语言。Domain SpericalLanguages:Unix排版语言(troff,eqn,tbl,pic,grap),shell使用语言(awk,sed,dc,bc)和软件开发工具(make,yacc,lex).领域特定语言和更灵活的应用程序的运行控制文件(sendmail,BIND ,X)之间,数据文件格式之间,脚本语言之间的界限都很模糊。

小语言,微型语言—保持设计的尽可能小,尽可能的简单。

领域特定的小语言是一种非常强大的设计理念,可以降低全局的复杂度。两种设计的方法,前两者是好的,第三个是危险的。

1.微语言可以将编程问题的规格说明提升一个层次,比通用语言更紧凑,更具表现力。

2.规则说明文件格式结构趋向复杂,控制应用程序中包含行为。判断是否规格说明是否描述控制流和部署?

3.通过扩展(危险),会引发语言特性蠕变。

所有复杂的规则说明书--->微型语言。设计微型语言:使用工具,学习技巧,记住佳例。

注意微语言所解决的特定领域问题。

理解语言的分类法

数据文件-->微语言

Glade:一种用于构建GUI界面的代码生成器。宏处理器m4:一种非常简单的申明性微语言(程序被表述成一套预订的关系和约束集,而不是明确的行为,用在微型语言的处理阶段)。

Unix的makefile是为了自动化编译过程而设计的,表达表达源文件和派生文件的之间的依赖关系以及从各个源文件生成派生文件的所需的命令,也是一种声明式的微语言。

用来描述XML转换的语言XSLT处于声明性语言的复杂度的顶端,其复杂到通常没有人认为它是一种微语言。

微语言的发展:申明性(具有隐式操作)-->命令性(具有显式操作)-->通用解释器

完备图灵机---可以进行条件和循环(递归)操作,具有用于控制结构的特性。

EmacsLisp和JavaScript运行于特定上下文的完全编程语言,嵌入式脚本;shell和Tcl具有简单的数据类型分类;Perl,Python和Java有着更复杂的类型系统。

通用解释器对其运行的上下文

应用微语言

两个挑战:何时直接应用微语言?何时为应用程序设计自定义微型语言?

Case study:Regular Expressions

正则表达式频繁出现在Unix工具和脚本语言中,是一种描述文本模型的一种声明性微语言,常内嵌到其他的微语言中。最简单的RE工具是grep。

Regexp符号的变种:

1.Glob表达式:*,[....],?.{}(bash和zsh添加的特性)

2.基本正则表达式:grep使用程序使用符号。

3.扩展的正则表达式:egrep,Lex和Emacs编辑器中使用正则表达式非常的接近egrep。

4.Perl正则表达式:这是Perl和Python的regexp工作所接受的符号。比egrep更加的强大

Case study:Glade

Glade是X的开源的GTK工具包所用的界面的创建程序。可视的GUI界面-->界面的XML文件-->C/C++,Python或者Perl代码

Glade用于描述GUI的XML格式是一个简单那的专门领域语言的好例子。

Glade格式的有效说明包含了响应用户行为的GUI指令表。1.Glabe的GUI把规格说明作为结构话的数据文件处理;2.Glade的代码生成器利用规格说明编写GUI的实现码。有运行时库的语言可以直接从XML文档实例化GUI,从而避免代码生成步骤。

Glade仅仅做了两件事:说明GUI窗口构建层次,关联窗口构件和属性。这种透明性和简单性是微语言设计良好的标识。符号和域对象之间的映射关系清晰,对象关系表达直接。

Case study:m4

m4宏处理程序解释描述文件转换的声明性微型语言。m4程序是一套宏命令集,支持条件和递归,一般用来作为预处理器。

Case study:XSLT

XSLT描述文本流变化的一种语言,进行宏替换以及XML数据的变换(查询和报表的生成),用于编写XML样式表的语言。

XSLT和m4都是纯声明性和完备图,灵机的语言,支持递归不支持循环。微语言的特征:

  • 有限的类型分类,没有记录结构和数组等类似结构。

  • 对外有限的结构。

《XSLT:MasteringXML Transformations》[Tidwell]

Case study: The Documenter‘s Workbench Tools

troff排版格式器是Documenter‘sWorkbench(DWB)的核心,其中各个组成部分都是不同类型的专门领域微型语言。GNU版本增强实现版groff。

troff是一个命令性微型语言,接近正式的解释器(具有条件和递归,没有循环)。

后端处理器(driver)通常是透明的,1970年后,这些都被整理成为设备无关的微型语言(device-independenttroff),用于在页面上插入文本呢和简单图形,转化为现代图像打印机能实际接受的格式—其中最重要的是Postscript。

预处理器的使用扩展了troff语言的能力,常用的三个语言是:制表的tbl,排版数学公式的eqn和用于绘制图表的pic。还有一些不常用的:制图用的grn及排版参数书面的refer和bib。

每个预处理器都是一个小语言,接受微型语言并将其编译成troff请求。通过查找特定的起始和结束请求,其余的标记则不加修改的传递,这样大多数预处理器通常能互不干涉地一任何次序运行(注意chem和grap都产生pic)。

DWB的文档标记在某些方面过时,但是预处理器所属的问题域表现了微型语言模型的能力—所见即所得的字处理器要具有同样的能力非常的困难。基于XML的现代文档标记语言和工具链任在试图赶上DWB在1979年具有的能力。

DWB强大的设计原因:所有的工具都共享一个通用文档的文本流的文本表达,格式化的系统划分成独立的组成部分,可以分别调试和改进。管道架构支持插入新的,实验性的预处理器和后处理器,而且不会干涉原有的处理器,系统是模块化且可扩展的。DWB工具是管道,过滤器和微处理语言结合模型范例。

troff本身是专用的命令性语言,当DWB的三个微语言工具是声明性的-->现代GUI工具包的理念:图像对象之间通过声明空间关系,然后让软件计算它们之间的最佳布局。

pic语言例子反应了微型语言一个常见的设计主题:使用微型语言解释器来封装某些形式的约束条件推理,然后将其转化为行为。

Case study:awk

awk是老式的Unix工具,设计目的:编写小巧但表达力的程序,将文本输入变换成文本输出。

awk程序:模式/行为,模式是正则表达式,行为是C语言的子集(变量,条件,循环以及类型分类)。awk和sed相似,运行时一行一行的过滤输入文件。

awk是图灵完备(TuringComplete1)的语言,可以读写文件,主要用做来报表生成器,尤其用于解释和减少列表数据。awk在复杂度和能力上取舍得不好,作为语言不紧凑,模式驱动组织了通用性。渐渐被新的派生的语言取代。

处理器和内存的成本的下降影响着微语言设计的权衡。通过限制能力来获取设计的紧凑性是个好注意,而通过限制能力节约设备资源不是,微语言的要不通用而不紧凑,要不紧凑而不通用,没有中间地带。

Case study:PostScript

PostScript是专门向成像设备描述排版文本和图形的微型语言。PostScript从头开始就是作为语言设计的,因此更加具有表达力,更有效,其编写的图形算法说明比其渲染出的位图更小,图灵完性(条件,循环,递归,具名过程)。PostScript是一种堆栈式的语言:其primitiveprocedure的参数通常是先进后出的参数堆栈,结果值重新入栈。400个左右的操作符中40个基本操作。堆栈式语言以紧凑,经济编码和可读性差而著名。PostScript通常被实现为打印机固件,Ghostscript可以将PostScript装换为各种图形格式和比较弱的打印机控制语言。大多数软件将PostScript看作是最终输出格式。

PostScript也是PDF的标准的组成部分。

Case study: bc和dc

dc是Unix上最古老的语言,在PDP-7上编写而成。无限精度算术,dc是一个逆波兰标记法计算器,bc是一个代数标记法计算器。dc和bc是图灵完备的,类型分类有限(无限精度整数和字符串),介于解释性微语言和完全脚本之间。dc/bc可对话式使用,支持用户定义程序库的能力--可编程的能力--命令性微型语言最重要的优势。

Case study:Emacs Lisp

专用的解释语言不仅可以作为从进程中运行专门任务,也可成为整个体系的核心。Emacs是围绕Lisp语言构建而成的,原语既可以说明编辑缓存的动作,也可以控制进程。

Emacs语言对于描述编辑动作和作为其他语言的前端很强大。可以通过“模式”(EmacsLisp编写的程序)来针对具体任务定制编辑器。

Case study:JavaScript

JavaScript是为了嵌入C程序而设计的一种开源语言,具有可以在网络服务器中嵌入可执行代码,提供给具有JavaScript能力的浏览器运行。

JavaScript是充分的图灵完备的解释性语言,和Python类似,具有整数,实数,布尔值,字符串和轻量级的字典对象。数值带类型,变量可容纳任意类的值,具有类Perl的正则表达式,首先的专用语言,通过DOM(DocumentObject Model)对象与客户端环境交互。

《JavaScript:The Definitive Guide》

设计微语言

微语言将问题规格说明书提升一个层次的方法。

《NotableDesign Patterns for Domain-Specific Languages》

选择正确的复杂度:

第一要素:Keepit Simple

微语言的设计不仅强大回报丰厚,同样也充满类似的陷阱。微语言的优势之一:将一些自顶向下的决策放到微语言的程序控制流中,从而帮助我们从自底向上的编程中得到一个良好的设计。给出语言错误信息的备选方法。

扩展和嵌入语言

基础性问题:能否通过扩展或者嵌入现有的脚本语言来实现自己的微型语言。这是实现命令性语言的正确的方法,不适和声明式语言。

在解释性语言(主语言)中编写服务函数实现命令性语言,微型语言作为脚本语言来加载服务库。Lisp中很常见,Tcl,Python和Perl可以存在。如有性能问题:C/C++编码,然后整合到微型语言中。

使用C代码扩展脚本语言or在C程序中嵌入脚本语言,取决于脚本语言本身。

编写自定义语法

是否可以使用XML作为语法基础并把XML规定为XML文档类型。或者考虑使用YACC和Lex生成。

宏(macro)—慎用

宏扩展机制是早期Unix语言设计者青睐的策略:C存在宏机制,pic中也使用宏机制,m4预处理器是现实扩展预处理器的通用工具。

在汇编程序中,宏经常是架构程序唯一的可用的方法。宏机制太过强大而且会被滥用。

语言还是应用协议?

Generation: Pushing the Specification LevelUpwards

The programmer at wit'send.... can often do best by disentangling himself from his code,rearing back, and contemplating his data,Representationis the essence of programming. --《TheMythical Man-Month》

数据比程序逻辑更易驾驭。无论数据是普通表格,说明性标记语言,模板系统还是一组可以扩展成程序逻辑的宏。将复杂度从程序代码中转移到数据中是好的实践,选择人维护和操作的数据作为表示法是好的实践。

更高级的,更富有说明力的标记法优点是可以利用编译期检查,可以更彻底的理解代码,和预期行为,是错误更容易被发现。

Unix程序包:数据驱动编程,代码生成器,特定领域的微语言等-->提升代码层次而使规格说明更简炼。

数据驱动编程

需要把代码和代码作用的数据结构划分清楚,在改变程序的逻辑结构是,就只需要编辑数据结构而不是代码。

数据驱动编程VSOO:1.数据驱动编程中,数据不仅仅是某个对象的状态,实际上还定义了程序的控制流;2.OO首先考虑的是封装,而数据驱动编程是为了编写尽可能少的代码。

进行任何类型的代码生成或数据驱动编程的重要原则是:始终把问题层次往上推,不要手工修改生成的代码或者中间形式。

数据驱动编程,最复杂的情形为p-code或者关于简单的微型语言编写解释器。类似与代码生成器和状态机编程,核心是:将程序逻辑从硬编定的控制结构转移到数据中。

专用代码的生成

Unix中有些强大的专用的代码生成器,用于构造词法分析器和语法分析器等,同样也有一些简单而且而且轻巧的多的代码生成方式可以使得生活更轻松。

原则:少干活,让数据塑造代码;依靠工具;把机制从策略中分离,建设性的懒惰是大师级的程序员的基本美德。

Configuration: Starting on the Right Foot

Let us watch our beginnings,and results willmanage themselvs.

--Alexander Clark

Unix的程序和周边的环境中交互的方式很多,大致分为1启动环境查询2交互通道。

什么是可配置的?

Unix中一切都是配置的,只要有可能,就将建立机制而把策略决定权交给用户。这种方式产生的程序的功能强大,专家非常顺手,接口的选项很多,配置文件像杂草一样疯涨。但是,什么不需要配置:1.可以进行自动检测的东西,不需要提供配置开关2.提供适应能力,0.7秒的延迟3.用户不应该看到优化开关4.可以使用脚本包装器或者简单管道实现的任务,不要配置实现。考虑这些问题:

  • 可以省略这个功能吗?为何要在加厚手册之外加重用户的负担?

  • 能否用某种无伤大雅的方式改变程序的常规行为从而无需这个选项?

  • 这个选项是否花哨没用?是不是因该考虑用户界面的可配置行而多考虑正确性?

  • 这个选项附加行为是否应该用一个独立的程序代替

增加不必要的选项会产生诸多不良后果,尤其是对测试覆盖率和测试量的影响。

配置在那?

  • /etc下的运行控制文件(或者系统中其他的固定的位置)

  • 由系统设置的环境变量

  • 用户主目录中的运行控制文件(以'.'开头的文件)

  • 有用户设置的环境变量

  • 启动程序的命令所传递的开关和参数

Interfaces:User-Interface Design Patterns in theUnix Enviroment

All our knowledge has its origins in ourperceptions. --Leonardo Da Vinci

程序的接口就是程序同人类用户以及其他程序通讯的方法的总和。用户接口代码通常占40%甚至更多的开发时间,所以需要分辨出良好的设计模式。主题是:1.与其他程序通讯的方式的前瞻性设计;2.最小立异原则-减少用户在使用接口时必须学习的复杂过程

程序启动后的数据来源:

  • 程序的标准输入端和命令

  • 通过IPC的输入,比如Xserver事件的网络消息

  • 已知位置的文件和设备(程序传递的或计算的数据文件名)

程序的输出以完全同样的方式发布结果。

键绑定的风格:vi或者emacs。

Unix接口设计的历史

1969-1980,CLI,1980以后出现rogue式程序,1985,向PaloAlto的GUI学习-->Macintosh-->windows.X window 系统(机制而不是策略),NetworkWindow System。

接口设计的评估:简洁,表现力,易用,透明和脚本能力

Optimization

Premature optimization is the root of all evil---C.A.R Hoare

关于性能优化的问题:Unix的经验就是如何知道何时不去优化。

什么也别做,就站在哪里

先估量,后优化

非定域性之害

吞吐量和延时

Complexity: As simple as Possible, but No Simpler

Everything should be made as simple aspossible,but no simpiler.

----Albert Einstein

贯彻设计的最重要的就是保持简单的设计,如何“尽可能的简单”?如何断定?这个需要自己断定。

复杂度的三个来源

简单即使美即雅,复杂即丑即恶。---Unix程序员的世界观---复杂就是成本。

1.程序员为了试图理解一个程序,从而建立其思维模型并调试该程序的困难度

2.顾客和用户的记忆负担

3.系统中的代码量—软件工程中最重要的衡量标准,与接口复杂度,实现复杂度可能同时上升。

如果设计时优先考虑容易实现或者代码规模,可能就会简单地把很多底层的任务抛给用户(manularitytrap)。为了保持代码适度规模的压力,不得不使用极端晦涩复杂的实现技法,

会增加实现复杂度,从而无法顺利调试(blivettrap)。过分专用陷阱(adhocitytrap).

Part III

Languages: To C or Not to C

The limits of my language are the limits of myworld.

Tractatus Logico-Philosophicus 5.6, 1918

Unix下语言的丰饶

Unix所支持应用程序语言,其范围比当今其他任何一种操作系统都要广泛。

两个原因:1.Unix广泛用于研究和教学平台2.应用设计和实现语言的合理搭配对生产力有极大的促进--->鼓励领域特定语言(DSL)和脚本语言-胶和剂。

Unix下的脚本语言:Perl,Python,Tcl-->强大独立的通用的语言。一些通用语言也出现了脚本的特性(Lisp,Java)。运行时完成完成解释是的动态存储管理的自动化相对容易,采用引用--->运行时多态。

有效的运行Unix哲学:C+其他的各种语言(特别是脚本语言),在大型程序系统中将担当各个专门角色的多个语言融合在一起。

警告:对于程序语言的选择是Internet/Unix世界应该认真考虑的原型问题。

为什么不是C

Unix世界中母语,1980以后,C/C++垄断编程10年。C/C++通过增加实现时间和调试时间为代价来优化效率(EquialencePrinciple)。急剧下降的成本,经济最优选择:减少调试时间,增加可维护性。

C/C++的核心的问题:程序员自己完成内存管理。C的内存管理是复杂性和错误的源泉。缓冲溢出是产生溢出和安全漏洞的常见问题。其他的内存管理的术语(http://www.catb.org/~esr/jargon).When the superior man refrains from acting,his force isfelt for thousand miles. (不言之教,无为之益,天下希及之--道德经)

使用脚本语言可以将生产力提高一倍,其性能的损失通常受到:I/O事件的影响,网络延迟以及缓存填充等等。Perl,Python。

解释型语言和混合策略

通过在运行期可执行体中嵌入一个内存管理器来完成内存的自动管理,运行环境:程序部分(运行脚本)和解释器部分。

Unixshell就是一个完全的解释型编程语言,使用shell编写胶和逻辑,把现有的公用程序同C编写定制程序结合成比各个部分总和更大的整体。高级shell编程可以自由混合语言编程,从数种或更多语言中为子任务开发二进制和解释型组件。

语言评估

混合语言是知识密集型的编程,语言应用知识,何时工作以及如何组合的应用知识。

C:Unix虚拟机的高级汇编器,gcc实现,c语言标准,最佳之处:资源效率和接近机器语言。最糟糕之处:资源管理的炼狱

C++:OO是银弹?,兼容性设计妥协,薄弱的标准化,OO的厚胶合层和维护问题,OO领域(GUI,多媒体工具包,游戏)。最佳之处:编译效率以及面向对象和范型编程的结合。最糟糕:非常的古怪复杂,往往过分的鼓励复杂的设计.实例分析:QT工具包,提供在X在编写图形界面的窗口构建和API,众多类库(XML,文件访问,套接字,线程,定时器,时间/日期处理,数据库访问,各种抽象数据类型和Unicode处理)

Shell:鼓励原型设计传统的来源,容易编写,是最简单的包装器。最佳之处:书写小型脚本非常的自然快捷。最糟之处:大型shell脚本必须依赖大量的辅助命令,这些辅助命令不一定在每个目标机器上都有实现。实例分析:xmlto--驱动脚本,

Perl:增强了的shell,作为替代awk而设计的,各种各样的perl模块(目录结构的遍历树,GUI的工具包,http机器人和CGI编程)。最佳之处:作为强有力的工具提供大量涉及正则表达式匹配的小型胶和脚本使用;最糟之处:当曾需很大的时候perl会变的非常的丑陋,刻板,几乎无法维护。实例分析:小-blg,大-keeper(3300)

Tcl:设计用来连入C编译库的小型语言解释器,提供C代码的脚本控制。内嵌脚本,Tcl的工具包:1.Tk-一种更亲切的更有友好的X的接口;Expect:一种更容易编写更多相应纯交互程序语言。Tcl扩展,脚本语言小而且有用,Tcl/Tk脚本。最佳之处:节俭,紧凑的设计和Tcl解释器的扩展性。糟糕之处:古怪的位置分析器和孱弱的数据结构和命名空间控制—难以适用大型程序。

Python:设计本意是与C语言紧密集成。动态载入C库程序中接受数据也可以向其传递数据,使用缩进控制代码块的结构。Python语言设计干净优雅,具有出色的模块化特性,发行包包含了Tk工具包,包括了大多数的重要的网络协议(SMTP,FTP,POP3,IMAP和HTTP)的客户类以及HTML生成器-->协议机器人和网络管理工具。协同开发大型复杂的项目中,Python和Java最佳。Jython,非常的慢,http://www.python.org,《ProgrammingPython》。最佳:鼓励清晰,易读的代码,易学易用,可以扩展为大型的项目;缺点:数度很慢。案例分析:imgsizer,fetchmailconf,PIL

Java:设计目的-”writeonce,run anywhere” and supportapplets。在系统编程和应用编程十分强大。动态内存管理,OO设计,完善的文档,《JavaIn A Nutshell》,《Thinkingin Java》,Kaffe实现,JDEE-基于Emacs的JavaIDE

EmacsLisp:脚本语言,文本编辑器,适用于Emacs编辑器以及其他的模式编辑和语法向导的交互式编辑。《TheGNU Emacs Lisp Reference Mannual》,《WritingGNU Emacs Extensions》。EmacsLisp的最佳之处在于结合了非常优秀的基础语言Lisp,其域原语对于文本操作非常的有效,但是性能比较的差。

未来的趋势

C/C++逐渐变得固守自己的优势领域,Perl+CGI,ASP,JSP,PHP

X工具包和语言的选择:Tk(资格老),GTK(原生C),Qt(最佳设计),wxWindows(原生C++)

Tools:The Tactics of Development

Reuse: On Not Reinventing the wheel

When the superior man refrains from acting,hisforce is felt for thousand miles. (不言之教,无为之益,天下希及之--道德经)

Portability: Software Portability and Keeping UpStandards

The realization that the operating systems ofthe target machines were as great an obstacle to portability as theirhardware architecture led us to seemingly radical suggestion: toevade the part of the problem altogether by moving the operatingsystem itself. --Portability of C programs and the UNIX System

UnixV6是一款可移植性的极高的生产级操作系统.移植性是Unix的主要的优势,Unix程序员设想硬件是易变的,只有UnixAPI才是稳定的,尽可能少的假设诸如字长,字节顺序和存储体系等机器的特殊细节.C的抽象机器模型,避免依赖特殊易逝的技术,严格的遵守开发的标准。

可移植最直接的效益是工具和应用程序无需每隔几年就重写,Unix软件比原生的硬件平台长久.可移植的戒律往往在架构,接口和实现上施加简单化的影响-->提高了项目成功的几率降低软件生命周期的维护成本。

C的演化

C语言及其附带服务接口(标准函数库)的稳定性一直以来都是Unix编程实践的核心.1973年诞生的语言,30年频繁使用,很少变动需求,堪称奇迹。

早期的C:Unix移植到PDP-11的系统变成语言,基于KenThompson早期的B语言解释器,脱胎于BCPL(BasicCommon Programming Language).

DMR编译器—WhitesmithsC编译器—PCC(StevenC.Johnson)

参考书《TheC Programming Language》

C语言标准:保守的过程,原始C语言的精髓得以小心保留。X3J11ANSI委员会,C83,C89,C9X

Unix标准

1973年,Unix使用C语言重写后,变得史无前例的容易移植和修改.1985年,Unix标准,UnixAPI文档。

标准和Unix之战:AT&T和Berkeley阵线上的分裂。Unix内战中,技术标准成为互相合作技术人员的推动的东西。IEEE(POSIX)---X/Open(SingleUnix Specification,SUS)--1999年X/Open加入了POSIX活动。

开源世界的Unix标准:Linux是从头到尾的按POSIX的UnixAPI标准来设计的。

新unix学派的的兼容性不是源代码而是二进制兼容性。廉价386PC机干掉了小型机和工作站,API是稳定的,系统管理文件的预期位置,实用程序,用户邮箱名和日志文件的前缀路径经常发生变化-->文件系统层次标准(FilesystemHierarchy Standard,FHS)--Linux Standards Bases—Free Standards Group

IETF和RFC标准化过程

Unix社区同互联网工程师文化相结合,从而继承了InternetEngineering TaskForce的RFC标准化过程中形成的思维。IETF传统,标准必须来自于一个可用原型实现的经验—一旦成为标准,同标准不一致的代码就被认定为不和规范的,必须抛弃。

规格DNA,代码RNA

PDP-7时代,Unix程序员就比其他程序员更倾向认为代码是可以抛弃的。Unix文化主张拆毁了重来。代码作为标准的从属物来思考,标准让程序可以协作,将各项技术结合起来成为比部分之和更大的整体。IETF标准化就是为了获得最佳的实践。ABSI/ISOC虽有错误,但是异常的简洁实用;SingleUnix Specification包含了30年的实践和改进,比ANSIC更为凌乱,但是有Linux的实践的证明.

尊重已颁布的IETF以及其他的相关的标准。原型循环不断的测试和演进的开发方法。

良好的规格说明具有巨大的价值,其演进是通过草案+实践实现的。程序文档就是近似与规范主体的”提议标准”.Unix开发,文档在开发之前,或者是同程序一起编写.X11的C核心标准在第一版之前就完成了。良好的规格说明书,开发X测试套件更加的容易。半自动化的测试套件是X系统的一个主要的优势,规格说明是代码的高级的形式的表现。

标准先行的态度:1.构建优良的测试套件2.系统行为的争论可以在规格功能的层面解决,避免了实现层次的问题的纠缠。3.应用程序的长寿

开放标准和开放源码之间的关系--->使用标准知识来获得软件可移植性的实践问题。

可移植性编程

软件的可移植性通常是准空间的问题:代码可以从其诞生的环境迁移到别的硬件和软件平台吗?在移植性编程中,尝试考虑选取最有可能持续的软件环境作为构建软件的基础,避免使用不久的将来可能会消亡的技术。Unix二十年详述了可移植性API问题的关注,最终通过了SingleUnix Specification来解决了。

但是并不是所有平台依赖性都同系统和库函数API相关,实现语言的问题,源系统和目标系统之间文件系统设计和配置的不同也是个问题。

可移植性和编程语言选择

可移植性的首要问题是实现语言的选择,这里移植性问题主要指得的(IPC,并发进程管理以及GUI的基础设施)

C的移植性

C语言核心的可移植性非常的高,标准的Unix实现是GNUC编辑器。标准输入输出,数学例程以及国际化支持可以移植到所有的C实现中,仅仅使用“singleUnixSpecification”中描述的现代API,文件I/O,信号,进程控制。涉及IPC,线程以及GUI接口时,移植性的问题会很严重.很多的GUI工具包具有一定的跨平台的特性:Tk,wxWindows,GTK以及Qt。

C++的可移植性

在操作系统层次上,C的所有问题C++都有。C++增加一个是开源的C++编译器已经落后于专有的实现.没有存在一个像GNUC这样的事实的标准,没有一个C++编译器实现了C++99ISO语言标准,GNUC++是最接近的一个。

Shell的可移植性:语言不是问题,主要的是各个不可移植的命令和过滤器,这个问题非常的严重,重量级的编程-->Perl,Python,Tcl等第二代脚本中

Perl的可移植性:核心版本不成问题,有时需要请求CPAN(ComprehensivePerl Archive Network)的插件库,无法保证在每个Perl实现中都存在。

Python的可移植性:可移植性及其出色,Tk工具包,可以支持Unix,MacOS和window的GUI。Python的主版本存在比Perl更丰富的标准函数库,重要的扩展模块随主干Python的次要版本一起发布.

Tcl的可移植性:整体来说不错,随着项目的复杂度不同而有极大差异,GUI的Tk包原生于Tcl,扩展的模块的问题比Perl要严重一些.

Java的可移植性:可移植性特别出色,但是问题是JDK1.1+AWT GUI包和JDK1.2以及SwingGUI之间的版本的兼容性问题比较麻烦:

  • Sun的AWT设计不完善,不的不用Swing来代替

  • 微软拒绝在windows中支持Java开发,并试图使用C#取代Java

  • 微软在IE中保留对JDK1.1的Applet的支持

  • Sun许可证让JDK1.2的开源实现不太可能,延缓了其部署进程(Linux世界)

Emacs Lisp的可移植性:

避免系统依赖性

选定了语言和支持库后,下一个移植性问题是系统关键文件和目录的放置.可以通过退一步重新架构来避免这个问题。

移植工具:GNUautoconf—系统探测,定制出makefile文件,configure;make; make install;X window系统中的Imake,LarryWall的Configure

软件国际化

软件代码的国际化设计使得接口容易适应多种语言各种字符集.Unix中关于国际化良好实践:1.分离信息库和代码。工具GNUgettext 2.原生个的使用UTF-8,XML和Java实际促进了这个实践。3.使用正则表达式的时候,当心字符的范围.

可移植性,开放标准以及开放源码

可移植性需要标准,而基于开放源码的实现是已知的最有效的办法。保证源代码移植性的最有效的办法是不依赖于专有的技术。互联网改变了一切,开源运动.

Documentation:ExplainingYour Code to a Web-Centric World

I‘ve never met a human being who would want toread 17,000 pages of documentation,and if there was, I'd kill him toget him out of the gene pool.

--Joseph Costello

Unix的第一个应用,文档整理平台,计算机驱动排照,文档格式软件,排版软件,版面设计程序,万维网的发明—文档实践—一切皆HTML,所有的引用都是url,Unix辅助浏览器:解析确定特殊种类的URL的网页浏览器(man,ls).

文档概念

所见及所得的文档程序和以标记为中心的工具的差异:大多数的桌面出版程序和字处理软件属于WYSIWYG;以标记为中心的系统中,主文本通常包含明确可见控制标记的纯文本,其源文件可以由文本编辑器修改,显示和打印的时候必须传入一个特定的格式器程序。

Unix的原生文档工具:以标记为中心。WYSIWYG风格的实现往往很多情况下工作的不是很好,表面原因,深层原因,屏幕和打印的问题。WYSIWYG实际依赖标记码,通过额外的努力将其隐藏在使用中,违反了透明原则。WYSIWYG不适和处理数量巨大的文档的版面的设计。

万维网和XML-->表现方式(文档外观的指令)和结构标记(文档如何组织,有何用意的指令)在文档中区别开来。

表现级标记在文档本身中携带所有的格式信息(空白布局和字体变化)。结构标记系统中,文档必须和一个样式单(告诉格式器如何将问文档中的结构标记转换为物理版面的布置)结合。如果希望打印和网页生成格式都很良好,结构标记必须通过一个高级别的间接层。

大多数以标记为中心的文档系统支持宏(宏是用户自定义命令,通过文本替换来扩展内嵌的请求标记序列,宏标记语言—结构特征)。小文档和大文档的区别。

Unix风格

Unix风格的文档具备几个技术和文化的特征。

1.大文档偏爱:对不透明的二进制文档格式的厌倦,PostScript(图像打印行控制语言标准),PDF,图像支持的力度比较的差,图表,图形,数学方面的支持很强.Unix工具发展历史:表现性标记--->结构性标记,HTML,多媒体文档支持,XML

2.文化风格:Unix文档是由程序员写给程序员的,简洁而完善(BUGS),Unix程序员编写参考书籍,

各种Unix文档格式

troff(表示层标记语言)和Documenter'sWorkbench Tools(设计用来处理技术文档的工作),troff各种各样的宏指令

TEX:威力强大的排版程序,图像处理,准确内容定位,以及国际化方面,LATAX,隐蔽用法:XXX文档--->LATEX--->PostScript,表现级文档

Texinfo:GNU的文档标记,用来管理GUN项目文档(Emacs,GCC编译器簇等),可以生成HTML,Texinfo-->DocBook

POD(Plain Old Documentation):Perl支持者使用标记系统-->手册页

HTML

DocBook:为大规模,复杂技术文档而设计的SGML和XML文档类型定义。DocBook纯粹的结构性的标记语言,支持转换到HTML,XHTML,PDF,Windows帮助,PostScript.几个主要的开源项目(Linux文档项目,FreeBSD,Apache,Samba,GNOME,KDE)使用DocBook作为首要格式。

当前Unix社区中的文档管理是混乱的,目前朝着XML-DocBook格式的发展。DocBook的问题:各种暗语,以及许多缩写,XML标准和技术文章声名狼藉。

文档格式器,将样式应用到文档中,并让它美观大方,就必须知道文档的整个结构。定义文档类型定义或者DTD。DocBook为XML的方言,是一个巨大的DTD(400个标签)。

注意:格式器需要两个隐含的输入是DTD和样式表。

各种其他的DTD:TEI—大型精细的DTD,主要使用在学术界书稿文本的计算机电子稿件格式.XHTML—最新的HTML,也是有DTD描述的一个XML应用,其工具链将HTML-->纯ASCII的网页浏览器.www.xml.com/pub/rg/DTD_Repositories.

DocBook工具链:DocBook-->XHTML(xmlto,引擎-xsltproc|Saxon|Xalan)

Unix文档的最佳实践

为读者负责,信息密度适中.如果项目比较大:手册页,教程手册,常见问题解答列表。XML-DocBook维护主文档,发布XML主文档,项目安装包支持ScrollKeeper,从主文档生成XHTML并将其放到项目的网页上提供访问。

Open Source: Programming in the New UnixCommunity

Unix和开发源代码

开源开发利用了这样的事实,甄别和修改bug的任务适合分解成多个并行的子任务。原型设计,领域开发。

开源开发的规则:

1.源码公开:鼓励第三方的同行复审

2.尽早发布,经常发布:反馈迅速而且有效,项目的启动版本应该是可以编译和运行的。

3.给贡献以表扬:如果不能给物质奖励,就给予精神奖励,人们是为了展示才华而努力工作的。注意:发布是例行公事,频繁发布的原因是缩短和加速同用户和开发者之间的反馈循环。

开源项目的交流主要是通过email和网络文档(wiki).核心开发者或者核心开发组指导项目,其他志愿者零星贡献.

开源项目遵循Unix传统尽可能的自动化的建议,使用CVS这样的版本控制工具来网络访问代码.

与开源开发者系统工作的最佳实践

1.良好的修补实践

2.良好的项目,档案文件命名实践

3.良好的开发实践

  • 不依赖专有代码

  • 使用GNU自动工具

  • 先测试在发布代码

  • 发布前对代码进行健全检查

  • 发布前对文档和README进行拼写检查

  • 推荐C/C++移植性实践

4.良好的发行制作实践

5.良好的交流实践

许可证的逻辑:如何挑选

许可证涉及开源社区核心价值近似神圣的盟约.

公共领域,MIT|XConsortium,BSD,Artistic许可证(Perl),GPL|LGPL,Mozilla

Futures: Dangers and Opportunities

The best way to predict the future is to inventit. --Alan Kay

1.In computabilitytheory,a system of data-manipulation rules (such as an instructionset,a programminglanguage,or a cellularautomaton)is said to be Turingcomplete or computationallyuniversal ifand only if itcan be used to simulate any single-taped Turingmachine andthus in principle any computer.

Unix哲学学习笔记相关推荐

  1. 佛教哲学 学习笔记-002-法体有恒-有部哲学

    欢迎关注公众号: [本课题要] 西方人的思维是解剖式的.分析式的.逻辑式的,而东方人的思维是整体式的.综合式的.统一式的,东方的哲学喜欢以一贯一切,以一贯百,一通百通,其实这从中国古代儒家的天人合一观 ...

  2. 佛教哲学 学习笔记-06-唯识古今-唯识学(上)

    [本课提要] 按思想体系来分,唯识学存在两种分类:第一种分类法叫古今分类法,第二种分类法叫新旧分类法.本课先来简单介绍一下唯识学的古今分类法. 006.唯识古今--唯识学(上).mp3音频:00:00 ...

  3. 佛教哲学 学习笔记-07-新旧唯识-唯识学-中

    [本课提要] 所谓唐僧去取经,其实就是去解决关于人的识,心识,到底是几个识?是八识还是九识的问题.取的是谁的经?是唯识今学大师陈那的经. 不管是旧唯识,九识说,还是新唯识,八识说.其实从域外传入中国以 ...

  4. 佛教哲学 学习笔记-08-唯识一心-唯识学-下

    [本课提要] 吴老师点评:"识"的解释体系太庞大,最后变成了只有少数人懂的形而上,严重影响了传播,所以必须做适合老百姓理解的简单概念.这是传播的需要. 上一课讲到中国对唯识学改造的 ...

  5. 佛教哲学 学习笔记-003-四谛归空-空宗哲学

    欢迎关注公众号·: 这一课我们来讲空宗哲学.空宗哲学是佛教哲学四大哲学体系中的第二种,它本身是从佛教第二次大分裂中的划地部中产生的,空宗哲学的基本命题是:四谛归空.就是将佛教的全部真理,最终归结为空. ...

  6. Unix C学习笔记10 进程的概念,相关命令,父子孤尸,进程标识,创建子进程

    进程的概念 相关命令 pstree显示bash进程 ps显示进程,以快照方式,显示某一瞬间进程状态 ps aux top显示实时进程信息,会动态变化 父子孤尸 进程标识 会重用,但不会立马重用PID ...

  7. Unix原理与应用学习笔记----第六章 文件的基本属性2

    Unix原理与应用学习笔记----第六章 文件的基本属性2 改变文件权限命令:chmod 提示:文件或目录创建后,就被赋予一组默认的权限.所有的用户都有读,只有文件的所有者才有写. 相对权限设置 Ch ...

  8. 【学习笔记】Windows格式文档转换成Unix格式

    [学习笔记]Windows格式文档转换成Unix格式 我们有时候需要将文档格式从Windows格式转换成Unix格式,Windows下换行是\r\n,而Unix下换行是\n,所以只需要将文档中的\r去 ...

  9. 5w字总结 Unix系统编程学习笔记(面试向)(Unix环境高级编程/Unix环境程序设计)

    文章目录 一.计算 C语言的数据表示与处理 计算 C语言的基本运算操作 内存表和符号表 类型转换 函数类型的分析 指令 复合指令 句法 函数 函数激活(Activation Record) 函数激活定 ...

最新文章

  1. 美国多个城市禁止、我国却蓬勃发展的人脸识别是什么样子?
  2. linux块设备的IO调度算法和回写机制
  3. 18岁双料竞赛金牌得主邓明扬:我只是数学初学者,求在MIT“活”下去
  4. Swift函数_默认参数
  5. java 接口和抽象类的区别6_JAVA基础篇-接口和抽象类的区别
  6. cad常用字体包_水利设计CAD基础篇(一)
  7. 隐藏Android下的虚拟按键
  8. C#回顾 - 3.NET的IO:字节流
  9. Opencv--Mat图像基本操作
  10. linux镜像文件的rpm,REDHAT安装镜像里的RPM文件02
  11. 【HDU5299】Circles Game,圆的扫描线+树上删边游戏
  12. 如何取得事件中鼠标坐标
  13. CSS3 Flexbox 弹性布局
  14. linux 权限rwx(4,2,1)详细说明
  15. 【转】博弈论——acm
  16. 存储之磁盘阵列RAID
  17. 使用ArcMap 生成TPK和geodatabase包
  18. 嵌入式系统的概念,嵌入式系统的组成及特点,嵌入式系统的基本开发流程
  19. 产品经理 网文20年
  20. DAVSE VCC-H10004K超高清会议摄像机

热门文章

  1. 建立私有CA实现证书申请颁发
  2. Advanced Rails - Rails初始化20步
  3. hdu 4043 FXTZ II [ 概率 + Java大数]
  4. java读取word中的表格并存入到mysql数据库中实例
  5. 岚图高管解读近50亿融资:东风跟投9亿 考虑后续IPO
  6. 树莓派电信4G联网:树莓派3B+Quectel EC20
  7. JAVA安全之JAVA服务器安全漫谈
  8. ubuntu只读文件系统
  9. 日期/时间格式中的字母及其含义与示例
  10. couldn't set tty to ppp discipline invalid argument