《Unix编程艺术》 unix编程艺术 epub
题外:从老家从早到晚总算折腾回了杭州,进站太早,火车晚点,提包带断,什么倒霉事也遇上了,先发个已经整理好的部分,后续仍待整理。
多道程序设计:分离进程为独立的功能
无论在协作进程还是在同一进程的协作子过程层面上,Unix设计风格都运用“做单件事并做好的方法“,强调用定义良好的进程间通信或共享文件来连通小型进程,提倡将程序分解为更简单的子进程,并专注考虑这些子进程间的接口,这至少需要通过以下三种方法来实现:
1、降低进程生成的开销(思考下Erlang的process)
2、提供方法(shellout、IO重定向、管道、消息传递和socket)简化进程间通信
3、提倡使用能由管道和socket传递的简单、透明的文本数据格式
Unix IPC方法的分类:
1、将任务转给专门程序,如system(3),popen调用等,称为shellout
2、Pipe、重定向和过滤器,如bc和dc
3、包装器,隐藏shell管线的复杂细节。
4、安全包装器和Bernstein链
5、主/从进程
6、对等进程间通信:
(1)临时文件
(2)信号
(3)系统守护程序和常规信号
(4)socket
(5)共享内存,mmap
远程过程调用(RPC)的缺憾:
1、RPC接口很难做到可显,如果不编写和被监控程序同样复杂的专用工具,也难以监控程序的行为。RPC接口和库一样具有版本不兼容的问题,由于是分布式的,因此更难被追查。
2、类型标记越丰富的接口往往越复杂,因而越脆弱。随着时间的推移,由于在接口之间传递的类型总量逐渐变大,单个类型越来越复杂,这些接口往往产生类型本体蠕变问题。这是因为接口比字符串更容易失配;如果两端程序的本体不能正确匹配,要让它们通信肯定很难,纠错更是难上加难。
3、支持RPC的常见理由是它比文本流方法允许“更丰富”的接口,但是接口的功能之一就是充当阻隔点,防止模块的实现细节彼此泄漏,因此,支持RPC的主要理由同时恰恰证明了RPC增加而不是降低了程序的全局复杂度。
Unix传统强烈赞成透明、可显的接口,这是unix文化不断坚持文本协议IPC的动力。
ESR在这里还谈到XML-RPC和SOAP等协议,认为是RPC和unix对文本流支持的一种融合,遗憾的是SOAP本身也成为一种重量级、不那么透明的协议了,尽管它也是文本协议。
线程是有害的:
线程是那些进程生成昂贵、IPC功能薄弱的操作系统所特有的概念。
尽管线程通常具有独立的局部变量栈,它们却共享同一个全局内存,在这个共享地址空间管理竞争和临界区的任务相当困难,而且成为增加整体复杂度和滋生bug的温床。除了普通的竞争问题之外,还产生了一类新问题:时序依赖。
当工具的作用不是控制而是增加复杂度的时候,最好扔掉从零开始。
微型语言:寻找歌唱的乐符
(注,这里谈的微型语言,就是现在比较热门的词汇DSL)
对软件错误模式进行的大量研究得出的一个最一致的结论是,程序员每百行程序出错率和所使用的编程语言在很大程度上是无关的。更高级的语言可以用更少的行数完成更多的任务,也意味着更少的bug。
防御设计不良微型语言的唯一方法是知道如何设计一个好的微型语言。
语言分类法:
对微型语言的功能测试:不读手册可以编写吗?
现代微型语言,要么就完全通用而不紧凑,要么就非常不通用而紧凑;不通用也不紧凑的语言则完全没有竞争力。
一些引申想法:我认为这个评判标准也可以用在任何编程语言上,以此来判断一些语言,C语言既通用又紧凑,Java是通用而不紧凑,ruby、Python之类的脚本语言也是如此,正则表达式(如果也算语言的话)是不通用而紧凑,Erlang也是通用而紧凑,awk却是既不通用也不紧凑,XSLT也可以归入不通用不紧凑的行列;Javascript是个另类,按理说它也是不通用不紧凑,说它不通用是因为它的主要应用范围还是局限在web开发的UI上,实际上Javascript也是门通用语言,但是很少会有人会用javascript去写批处理脚本,Javascript显然是不紧凑的,太多的边边角角甚至奇奇怪怪的东西需要你去注意,然而就是这样一门不通用不紧凑的语言现在却非常有前途,只能说时势所然。
设计微型语言:
1、选择正确的复杂度,要的是数据文件格式,还是微型语言?
2、扩展和嵌入语言
3、编写自定义语法,yacc和lex
4、慎用宏,宏的主要问题是滥用带来的奇怪、不透明的代码,以及对错误诊断的扰乱。
5、语言还是应用协议。
posted @ 2010-02-22 00:46 dennis 阅读(2491) | 评论 (1) |编辑收藏
《Unix编程艺术》重读笔记(二)
模块性:保持清晰,保持简洁
软件设计有两种方式:一种是设计得极为简洁,没有看得到的缺陷;另一种是设计得极为复杂,有缺陷也看不出来。第一种方式的难度要大得多。
模块化代码的首要特质就是封装,API在模块间扮演双重角色,实现层面作为模块之间的滞塞点,阻止各自的内部细节被相邻模块知晓;在设计层面,正是API真正定义了整个体系。
养成在编码前为API编写一段非正式书面描述的习惯,是一个非常好的方法。
模块的最佳大小,逻辑行200-400行,物理行在400-800之间。
紧凑性就是一个设计能否装入人脑中的特性。测试软件紧凑性的一个简单方法是:一个有经验的用户通常需要用户手册吗?如果不需要,那么这个设计是紧凑的。
理解紧凑性可以从它的“反面”来理解,紧凑性不等于“薄弱”,如果一个设计构建在易于理解且利于组合的抽象概念上,则这个系统能在具有非常强大、灵活的功能的同时保持紧凑。紧凑也不等同于“容易学习”:对于某些紧凑设计而言,在掌握其精妙的内在基础概念模型之前,要理解这个设计相当困难;但一旦理解了这个概念模型,整个视角就会改变,紧凑的奥妙也就十分简单了。紧凑也不意味着“小巧”。即使一个设计良好的系统,对有经验的用户来说没什么特异之处、“一眼”就能看懂,但仍然可能包含很多部分。
评测一个API紧凑性的经验法则是:API的入口点通常在7个左右,或者按《代码大全2》的说法,7+2和7-2的范围内。
重构技术中的很多坏味道,特别是重复代码,是违反正交性的明显例子,“重构的原则性目标就是提高正交性”。
DRY原则,或者称为SPOT原则(singlepoint oftruth)——真理的单点性。重复的不仅仅是代码,还包括数据结构,数据结构模型应该最小化,提倡寻找一种数据结构,使得模型中的状态跟真实世界系统的状态能够一一对应。
要提高设计的紧凑性,有一个精妙但强大的方法,就是围绕“解决一个定义明确的问题”的强核心算法组织设计,避免臆断和捏造,将任务的核心形式化,建立明确的模型。
文本化:好协议产生好实践
文本流是非常有用的通用格式,无需专门工具就可以很容易地读写和编辑文本流,这些格式是透明的。如果担心性能问题,就在协议层面之上或之下压缩文本协议流,最终产生的设计会比二进制协议更干净,性能可能更好。使用二进制协议的唯一正当理由是:如果要处理大批量的数据,因而确实关注能否在介质上获得最大位密度,或者是非常关心将数据转化为芯片核心结构所必须的时间或指令开销。
数据文件元格式:
1、DSV风格,DElimiter-SeperatedValues
使用分隔符来分隔值,例如/etc/passwd
适合场景:数据为列表,名称(首个字段)为关键字,而且记录通常很短(小于80个字符)
2、RFC822格式
互联网电子邮件信息采用的文本格式,使用属性名+冒号+值的形式,记录属性每行存放一个,如HTTP1.1协议。
适合场景:任何带属性的或者与电子邮件类似的信息,非常适合具有不同字段集合而字段中数据层次又扁平的记录。
3、Cookie-jar格式。简单使用跟随%%的新行符(或者有时只有一个%)作为记录分隔符,很适用于记录非结构化文本的情况。
适合场景:词以上结构没有自然顺序,而且结构不易区别的文本段,或适用于搜索关键字而不是文本上下文的文本段。
4、Record-jar格式,cookie-jar和RFC-822的结合,形如
name:dennis
age:21
%%
name:catty
age:22
%%
name:green
age:10
这样的格式。
适合场景:那些类似DSV文件,但又有可变字段数据而且可能伴随无结构文本的字段属性关系集合。
5、XML格式,适合复杂递归和嵌套数据结构的格式,并且经常可以在无需知道数据语义的情况下仅通过语法检查就能发现形式不良损坏或错误生成的数据。缺点在于无法跟传统unix工具协作。
6、WindowsINI格式,形如
[DEFAULT]
account=esr
[python]
directory=/home/ers/cvs/python
developer=1
[sng]
directory=/home/esr/WWW/sng
numeric_id=1001
developer=1
[fetchmail]
numeric_id=4928492
这样的格式
适合场景:数据围绕指定的记录或部分能够自然分成“名称-属性对”两层组织结构。
Unix文本文件格式的约定:
1、如果可能,以新行符结束的每一行只存一个记录
2、如果可能,每行不超过80个字符
3、使用”#“引入注释
4、支持反斜杠约定
5、在每行一条记录的格式中,使用冒号或连续的空白作为字段分隔符。
6、不要过分区分tab和whitespace
7、优先使用十六进制而不是八进制。
8、对于复杂的记录,使用“节(stanza)”格式,要么让记录格式和RFC822电子邮件头类似,用冒号终止的字段名关键字作为引导字段。
9、在节格式中,支持连续行,多个逻辑行折叠成一个物理行
10、要么包含一个版本号,要么将格式设计成相互独立的的自描述字节块。
11、注意浮点数取整。
12、不要仅对文件的一部分进行压缩或者二进制编码。
应用协议元格式
1、经典的互联网应用元协议RFC3117《论应用协议的设计》,如SMTP、POP3、IMAP等协议
2、作为通用应用协议的HTTP,在HTTP上构建专用协议,如互联网打印协议(IPP)
3、BEEP:块可扩展交换协议,既支持C/S模式,又支持P2P模式
4、XMP-RPC、SOAP和Jabber,基于XML的协议。
透明性:来点光
美在计算科学中的地位,要比在其他任何技术中的地位都重要,因为软件是太复杂了。美是抵御复杂的终极武器。
如果没有阴暗的角落和隐藏的深度,软件系统就是透明的。透明性是一种被动品质。如果实际上能预测到程序行为的全部或大部分情况,并能建立简单的心理模型,这个程序就是透明的,因为可以看透机器究竟在干什么。
如果软件系统所包含的功能是为了帮助人们对软件建立正确的“做什么、怎样做”的心理模型而设计,这个软件系统就是可显的。
不要让调试工具仅仅成为一种事后追加或者用过就束之高阁的东西。它们是通往代码的窗口:不要只在墙上凿出粗糙的洞,要修整这些洞并装上窗。如果打算让代码一直可被维护,就始终必须让光照进去。例如fetchmail的-v选项将处理SMTP、POP的处理过程打印到标准输出,使得fetchmail行为具有可显性。
在“这个设计能行吗?”之后要提出的头几个问题就是“别人能读懂这个设计吗?这个设计优雅吗?”我们希望,此时大家已经很清楚,这些问题不是废话,优雅不是一种奢侈。在人类对软件的反映中,这些品质对于减少软件bug和提高软件长期维护性是最基本的。
要追求代码的透明性,最有效的方法是很简单,就是不要在具体操作的代码上叠放太多的抽象层。
OO语言使得抽象变得容易——也许是太容易了。OO语言鼓励“具有厚重的胶合和复杂层次”的体系。当问题领域真的很复杂,确实需要大量抽象时,这可能是好事,但如果coder到头来用复杂的办法做简单的事情——仅仅是为他们能够这样做,结果便适得其反。
所有的OO语言都显示出某种使程序员陷入过度分层陷阱的倾向。对象框架和对象浏览器并不能代替良好的设计和文档,但却常常被混为一谈。过多的层次破坏了透明性:我们很难看清这些层次,无法在头脑中理清代码到底是怎样运行的。简洁、清晰和透明原则通通被破坏了,结果代码中充满了晦涩的bug,始终存在维护问题。
胶合层中的“智能数据”却经常不代表任何程序处理的自然实体——仅仅只是胶合物而已(典型现象就是抽象类和混入(mixin)类的不断扩散)
OO抽象的另一个副作用就是程序往往丧失了优化的机会。
OO在其取得成功的领域(GUI、仿真和图形)之所以能成功,主要原因之一可能是因为在这些领域里很难弄错类型的本体问题。例如,在GUI和图形系统中,类和可操作的可见对象之间有相当自然的映射关系。
Unix风格程序设计所面临的主要挑战就是如何将分离法的优点(将问题从原始的场景中简化、归纳)同代码和设计的薄胶合、浅平透层次结构的优点相组合。
太多的OO设计就像是意大利空心粉一样,把“is-a”和“havea”的关系弄得一团糟,或者以厚胶合层为特征,在这个胶合层中,许多对象的存在似乎只不过是为了在陡峭的抽象金字塔上占个位置罢了。这些设计都不透明,它们晦涩难懂并且难以调试。
为透明性和可显性而编码:
1、程序调用层次中(不考虑递归)最大的静态深度是多少?如果大于四,就要当心。
2、代码是否具有强大、明显的不变性质(约束)?不变性质帮助人们推演代码和发现有问题的情况。
3、每个API中各个函数调用是否正交?或者是否存在太多的magicflags或者模式位?
4、是否存在一些顺手可用的关键数据结构或全局唯一的记录器,捕获系统的高层次状态?这个状态是否容易被形象化和检验,还是分布在数目众多的各个全局变量或对象中而难以找到?
5、程序的数据结构或分类和它们所代表的外部实体之间,是否存在清晰的一对一映射。
6、是否容易找到给定函数的代码部分?不仅单个函数、模块,还有整个代码,需要花多少精力才能读懂?
7、代码增加了特殊情况还是避免了特殊情况?每一个特殊情况可能对任何其他特殊情况产生影响:所有隐含的冲突都是bug滋生的温床。然而更重要的是,特殊情况使得代码更难理解。
8、代码中有多少个magicnumber?通过审查是否很容易查出代码中的限制(比如关键缓冲区的大小)?
隐藏细节和无法访问细节有着重要区别。不要过度保护。
无论何时碰到涉及编辑某类复杂二进制对象的设计问题时,unix传统都提倡首先考虑,是否能够编写一个能够在可编辑的文本格式和二进制格式之间来回进行无损转换的工具?这类工具可称为文本化器(textualizer).
宁愿抛弃、重建代码也不愿修补那些蹩脚的代码。
“代码是活代码、睡代码还是死代码?”活代码周围存在一个非常活跃的开发社团。睡代码之所以“睡着”,经常是因为对作者而言,维护代码的痛苦超过了代码本身的效用。死代码则是睡得太久,重新实现一段等价代码更容易。
posted @ 2010-02-19 19:25 dennis 阅读(2839) | 评论 (1) |编辑收藏
《Unix编程艺术》重读笔记(一)
Unix哲学是自下而上,而不是自上而下的,注重实效,立足于丰富的经验,你不会在正规方法学和标准中找到它。
Unix管道的发明人DougMcIlroy曾经说过:
1、让每个程序就做好一件事,如果有新任务就重新开始,不要往新程序中加入功能而搞的复杂。
2、假定每个程序的输出都会成为另一个程序的输入,哪怕那个程序是未知的。输出中不要有无关的信息干扰,避免使用严格的分栏格式和二进制格式输入。不要坚持使用交互式输入。
3、尽可能早将设计和编译的软件投入试用,哪怕是操作系统也不例外,理想情况下应该是几星期内,对抽劣的代码别犹豫,扔掉重写。
4、优先使用工具,而非拙劣的帮助来减轻编程任务的负担,工欲善其事,必先利其器。
RobPike在《Notes on Cprogramming》中提到:
原则1:你无法断行程序会在什么地方耗费运行时间。瓶颈经常出现在想不到的地方,所以别急于胡乱找个地方改代码,除非你已经证实那儿就是瓶颈所在。
原则2:估量。在你没对代码进行估量,特别是没找到最耗时的那部分之前,别去优化速度。
原则3:花哨的算法,在n很小的适合通常很慢,而n通常很小。花哨算法的常数复杂度很大,除非你确定n一直很大,否则不要用花哨算法(即使n很大,也要优先考虑原则2)。
原则4:花哨的算法比简单的算法更容易出bug,更难实现。尽量使用简单的算法配合简单的数据结构。
原则5:数据压倒一切。如果已经选择了正确的数据结构并且把一切组织得井井有条,正确的算法也就不言自明,编程的核心是数据结构,而不是算法。
原则6:没有原则6.
KenThompson对原则4做了强调:
拿不准就穷举。
Unix哲学的17条原则:
1、模块原则:简洁的接口拼合简单的部件。
2、清晰原则:清晰胜于机巧。
3、组合原则:设计时考虑拼接组合。
4、分离原则:策略同机制分离,接口同引擎分离。
5、简洁原则:设计要简洁,复杂度能低则低。
6、吝啬原则:除非却无他法,不要编写庞大的程序。
7、透明性原则:设计要可见,以便审查和调试。
8、健壮原则:健壮源于透明与简洁。
9、表示原则:把知识叠入数据,以求逻辑质朴而健壮。
10、通俗原则:接口设计避免标新立异。
11、缄默原则:如果一个程序没什么好说的,就沉默。
12、补救原则:出现异常时,马上退出,并给出足够错误信息。
13、经济原则:宁花机器一分,不花程序员一秒。
14、生成原则:避免手工hack,尽量编写程序去生成程序。
15、优化原则:雕琢前先要有原型,跑之前先学会走。
16、多样原则:绝不相信所谓“不二法门”的断言。
17、扩展原则:设计着眼未来,未来总是比预想来得快。
Unix哲学之一言以蔽之:KISS
Keepit simple,stupid!
应用unix哲学:
1、只要可行,一切都应该做成与来源和目标无关的过滤器。
2、数据流应尽可能的文本化(这样可以用标准工具来查看和过滤)。
3、数据库部署和应用协议应尽可能文本化(让人阅读和编辑)。
4、复杂的前端(用户界面)和后端应该泾渭分明。
5、如果可能,用c编写前,先用解释性语言搭建原型。
6、当且仅当只用一门编程语言会提高程序复杂度时,混用语言编程才比单一语言编程来得好。
7、宽收严发(对接收的东西要包容,对输出的东西要严格)
8、过滤时,不需要丢弃的消息绝不丢。
9、小就是美。在确保完成任务的基础上,程序功能尽可能的少。
最后强调的是态度:
要良好地运用unix哲学,你就应该不断地追求卓越,你必须相信,程序设计是一门技艺,值得你付出所有的智慧、创造力和激情。否则,你的视线就不会超越那些简单、老套的设计和实现;你就会在应该思考的时候急急忙忙跑去编程。你就会在该无情删繁就简的时候反而把问题复杂化——然后你还会反过来奇怪你的代码怎么会那么臃肿,那么难以调试。
要良好地运用unix哲学,你应该珍惜你的时间绝不浪费。一旦某人已经解决了某个问题,就直接拿来利用,不要让骄傲或偏见拽住你又去重做一遍。永远不要蛮干;要多用巧劲,省下力气在需要的时候用,好钢用到刀刃上。善用工具,尽可能将一切自动化。
软件设计和实现是一门充满快乐的艺术,一种高水平的游戏。如果这种态度对你来说听起来有些荒谬,或者令你隐约感到有些困窘,那么请停下来,想一想,问问自己是不是已经把什么给遗忘了。如果只是为了赚钱或者打发时间,你为什么要搞软件设计,而不是别的什么呢?你肯定曾经也认为软件设计值得你付出激情……
要良好地运用unix哲学,你需要具备(或者找回)这种态度。你需要用心。你需要去游戏。你需要乐于探索。
操作系统的风格元素:
1、什么是操作系统的统一性理念
2、多任务能力
3、协作进程(IPC)
4、内部边界
5、文件属性和记录结构
6、二进制文件格式
7、首选用户界面风格
8、目标受众
9、开发的门坎
更多阅读
2015年6月16日《枣强新闻》播出《我县召开老年书画研究会工作会议 中国书画艺术研究会
2015年6月16日《枣强新闻》播出我县召开老年书画研究会工作会议本台消息:6月13日,我县老年书画研究会工作会议在县老干部活动
李白《蜀道难》审美艺术赏析 李白 蜀道难
李白《蜀道难》审美艺术赏析 《蜀道难》是中国唐代伟大诗人李白的代表作品。全诗二百九十四字,以山川之险言蜀道之难,给人以回肠荡气之感,充分显示了诗人的浪漫气质和热爱祖国河山的感情。写景状物以及诗歌体现出来的气象的宏伟而阔
泰国鬼片第六位之《恶魔的艺术3鬼影随行》 恶魔的艺术3 鬼影随
因果轮回取决于我们自身的罪孽,罪孽之间环环相扣。降头术好比一把刀子,刀子本身并不邪恶,看什么样的人来用。苦海无边,回头是岸。如果你还不回头,便追悔莫及。你不要忘了,这是你自己的抉择!《恶魔的艺术3鬼影随行》,泰国血腥类恐怖片的
从春晚舞蹈《雀之恋》看舞蹈艺术 春晚舞蹈
摘要2012年龙年春晚无论是舞台设计,还是舞美和演出都打破了传统模式,正常演出都围绕着“爱”和“情感”来展现,其中杨丽萍主演的舞蹈《雀之恋》成为晚会的一大看点,舞蹈的服装、配乐、舞美等都堪称经典。舞蹈演员一亮相就惊艳全场,服装
《史晨碑》的艺术风格及学习方法
《史晨碑》为著名的汉碑之一。前后碑字体如出一人之手,传为蔡邕书。结字工整精细,中敛而四面拓张,波挑分明,呈方棱形,笔致古朴,神韵超绝,为汉隶成熟期方整平正一路书法的典型,对后世有深远的影响。明郭宗昌谓其“分法复尔雅超逸,可为百代模楷