UNIX编程艺术笔记

1.6 UNIX原则

组合原则

简洁原则

透明性原则

经济原则

1.7 KISS原则

Keep It Simple,Stupid

1.9 态度也要紧

看到该做的就去做,短期来看似乎是多做了,但从长期来看,这才是最佳捷径

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

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

Unix要繁荣,就必须采用吸纳低价而灵活的方案的诀窍,而不是去反对他们

318-327看完

4 模块性

软件有两种设计方式,一种设计得极其简洁,没有看得到的缺陷;一种设计得极其复杂,看不出缺陷,第一种难的多

要编写复杂软件又不至于一败涂地的唯一方法,就是用定义清晰的接口把若干简单模块组合起来,这样多数问题只会出现在局部

4.1 最佳模块大小

一些最有能力的开发者,一开始总是定义接口,然后编写简要注释,对其进行描述,最后才编写代码,因为编写注释的过程,就阐明了代码必须达到的目的

模块之间通过API接口通信,bug最少的最佳物理行在400到800,关于逻辑行和物理行见下面代码

a="我是一个物理行"
a="""我是一个逻辑行 
因为我一条语句便跨越了2个物理行"""

4.2 紧凑型和正交性

4.2.1 紧凑性

就是一个设计能否装进大脑的特性: 用经验的用户通常需要操作手册吗?如果不需要,那么这个设计就是紧凑的

经验法则: 记忆的条目数大于七,则不太可能是紧凑的

4.2.2 正交性

任何操作均无副作用,只会改变一件事,而不会影响其他

体现了 单一职权原则  只做好一件事

正交性代码更容易复用

Don't repeat youself 当你修改重复部分时,你可能只是修改了一部分而非全部,非常危险

设计最好围绕一个强核心算法,就比如,grep围绕正则表达式

完美之道,不在无可增加,而在无可删减

设计方法有自顶向下和自底向上,unix采用自底向上

为了中和自顶向下和自底向上,我们需要胶合层,类似于分层的api,service,common层

而且胶合层应该尽量的薄,比如C语言,就是硬件和软件的胶合层,而C语言的风格就是尽量简洁,功能尽量少

API的入口点尽量不超过七个,简单的API比复杂API更好

5 文本化:好协议产生好实践

5.1 文本化的重要性

文本文件/协议优于二进制流文件/协议,因为透明性(用户易读懂)

这一章就是一堆文本格式的介绍 RFC 822(电子邮件格式,包括 to,subject,cc等) 和XML

以及Unix文本格式的约定

看到 5.3 应用协议设计。 有点看不下去,好无聊,也看不太懂

看到490页,5.4应用协议元格式,这章完全讲协议啊,不看了

473~490

511~554

第六章

美在计算机科学中的地位,比在其他任何技术中心的地位都重要,因为软件太复杂了。美是低于复杂的终极武器。

可显性,仅仅做到不晦涩是不够的,还必须尽力做到有帮助,比如有意义的函数名称,或者出现故障时有用的日志信息,比如gcc编译时,打出来的一堆调试信息,易于帮助我们定位,哪里出了问题

透明性,就是我们一眼就知道,这个机器(或者代码)在干什么

编写透明、可显得系统而节省的精力,将来完全可能就是自己的财富

Window注册表,所有注册记录都储存在一个大文件中,这是不好的,大文件应该分为多个小文件

策略和机制应该分离,策略,就好比你玩家在游戏中的装备等数据,而服务器引擎则是机制

Freeciv这个游戏,其配置文件类似于windows注册表,但是它的配置文件只能由游戏开发者改动,这就避免了用户改动注册表,导致的注册表过长,运行速率过慢。

6.2 为透明性和可显性而设计

这个设计能行吗?

别人能读懂这个设计吗?

这个设计优雅吗?

这三个问题不是废话,优雅不是一种奢侈,这些品质对于减少bug和提高软件长期维护性是最基本的

6.2.2 透明性之禅

最有效的方法很简单,就是不要再具体操作的代码上叠放太多抽象层???  这也是为什么要有薄胶合层

优秀Unix代码的简洁依赖于严格自律和高水平技艺

6.2.2 为透明性和可显性

去欲望,少依恋,如实见

程序调用的最大静态深度是多少,如果大于四,就要当心

每个API的各个函数调用是否正交? 或者存在太多的特征标志?

是否存在一些顺手可用的关键数据结构或全局唯一的记录器

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

透明性和避免过度保护

隐藏细节和无法访问细节有着重要区别(比如debug时能打出隐藏的日志)

良好程序应该有调试和探测开关

6.2.4 透明性和可编辑的表现形式

尽量实现可编辑的文本和二进制格式来回无损转换

CLI (comand line interface)命令行接口程序

6.2.5 透明性、故障诊断和故障恢复

透明的更容易恢复,也更健壮

6.3 可维护性

如果作者以外的其他人能顺利理解和修改软件,那么软件就是可维护的

Unix哲学:宁愿抛弃、重建代码也不愿修补哪些蹩脚的代码

可维护的两个重要实践

选择简单的算法,“拿不准就穷举”,复杂的算法容易出bug,而且难以维护

还有就是,开发者手册,简略描述代码的关键数据结构和算法

7 多道程序设计: 分离进程为独立的功能

Unix的艺术: 将大型程序分解成多个协作进程

做单件事并做好

shell执行,就是创建多个协作进程,并由管道连接

IPC 进程间通信

7.1

过早优化是万恶之源

线程不是降低而是提高了全局复杂度,因此,除非万不得已,尽量避免使用线程

一个程序划分为多个协程,也更利于安全性,只有较小的协程去执行特权指令

最简的就是最好的

7.2 Unix IPC方法分类

7.2.1 把任务转给专门程序

专门程序,其实就是不和其他进程通信,只做自己的事情的程序

7.2.2 管道、重定向和过滤器

管道 就是 做单件事并做好

8 微型语言

8.1 理解语言分类法

结构或用的数据格式文件(比如/etc/passwd) -> 微型语言(比如make) -> js -> Java

  

UNIX编程艺术笔记_第1张图片

这些语言一定程度上是解释器,解释器范畴越大(即所需要的上下文越少)越通用(越靠右),

8.2.1 sng

png没有透明性,sng却能够将png以json字符串的样式表现出来,所以具备了透明性

8.2.2 正则表达式

微语言的一种,极其简练却很有用

8.2.3 Glade

透明性和简单性

8.2.4 m4

宏命令: define 'OS' 'operation system'

但是谨慎用宏

8.2.5

图灵完备机

其实语言就是解释器?

最小立异原则

必须谨慎使用语法糖,以免造成的晦涩多于帮助

8.2.8 awk

unix下输入 info gawk,获取在线文档

9 生成:提升规格说明的层次

程序员束手无策.... 只有跳脱代码,直起腰,仔细思考数据才是最好的行动,表达是编程的精髓(????表达力吗)

人类其实更善于观察数据,而不是推导控制流程(我觉得,人类是视觉动物)

所以,尽可能的把设计的复杂度从程序代码转移到数据,是个好办法

过程式语言 c,

说明式语言 sql

相对来说编译时,c更难分分析,sql更容易分析(我觉得,透明性),容易分析的语言,更容易找出错误

进行数据驱动编程(和面向对象编程不同)时,需要把代码和代码作用的数据结构划分清除,这样,在改变程序的逻辑时,只要编辑数据结构而不是代码,例子:ascii转译程序,只需要码表和map逻辑

9.2 专用代码的生成

9.2.1 ascii 代码

assic 用法屏幕,就是作者自己手写一整个表,然后让代码去打印这个表, 这样的话,要改输出格式,只要对这个手写的表进行操作就行啦,NB666

UNIX编程艺术笔记_第2张图片

9.2.2 为列表生成HTML代码

我草,这个真是屌爆了

UNIX编程艺术笔记_第3张图片

 而我们可以先把数据按以下格式存储在文档中

UNIX编程艺术笔记_第4张图片

 然后通过sed,生成对应的html列表

我草,太吊了

所有这些例子的借鉴之处都是一样的:

尽可能少干活;让数据塑造代码;依靠工具;把机制从策略中分离 

建设性的懒惰是大师级程序员的基本美德之一

编码尽量往上推以最小化常数缺陷密度效应??????

10 配置: 迈出正确的第一步

积硅步,至千里

10.1 什么应是可配置的

什么应是可配置的?  回答:全部

分离原则: 只要可能,就建立机制而把策略决定权交个用户

尽量用自动检测来减少配置开关的数量

让程序经济运行时设计者的任务,用户不应该看到优化开关

考虑一下问题:

能省掉这个功能吗

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

这个选项是否花哨没用?

增加一个开/关配置选项,就是使测试量加倍,既然在实践中从来没有人完成双倍测试量,那么实际影响,就是减少了特定配置获得的测试量

10.2 配置在哪里

1 /ect

2 系统环境变量

3 用户主目录(LINUX中的~)的运行控制文件

4 用户环境变量

5 命令行参数

后者覆盖前者,越往后范围越小

10.3 运行控制文件

rc 后缀,代表'运行控制(run control)'

其实运行控制文件和点文件(比如.netrc文件, .profile文件),我的理解就是配置文件,用来决定程序按照何种配置去运行

10.3 命令行选项

-a -ab代表组合-a和-b

10.5.1 从-a到-z的命令行选项

-a -all所有

-b buffer或者batch

-c command 命令(带参数)

-d debug 调试,偶尔delete

-e execute

-f file 代表输入文件,输出文件

-i interactive

-k keep或者kill

-l list

-n number

-o output

-p port 

-q quite

-r recurse递归 reverse反向

-z zip启用压缩

看到881页

10.6.1 实例分析fetchmail

提供了了太多的选项,以至于用户手册过于复杂

解决办法就是提供-o选项,然后-o后面的参数视为配置文件的一行信息

10.6.2

11 接口: Unix环境下的用户接口设计模式

我们所有的知识都来源于我们的感知

与其他程序通讯方式的前瞻性设计,这是什么鬼?????

最小立异原则: 如果可能,尽量允许用户将接口功能委派给熟悉的程序来完成,比如让用户选择自己的文本编辑器

互助式的接口组合使用

共生和委派策略来提高代码的复用度,并降低软件复杂度: 让用户选择自己的代理

如果不能委派,那么就效仿

11.3 接口设计评估

简洁、表现力、透明、易用、脚本化

简洁: 可以用操作时间来衡量,键盘拼写比鼠标点击屏幕字符要快得多

表现力:可以触发相当广泛的行为,比如键盘可以单键,也可以组合键

易用性:和用户要记忆的东西成反比

接口透明度:所见即所得,中间结果也很容易看出来

脚本化: 接口更容易为其他程序所用

自动完成重复任务:

roguelike

更具表达力的语言意味着程序更短,bug更少。

过滤器原则
Postel原则 宽进严出
过滤时,不需要的信息也绝不丢弃
过滤时,绝不增加无用数据 比如不遵循格式的日期,空行

Cantrip模式
没有输入没有输出,启动时指定条件,具备很强的脚本能力

比如 clear rm命令

比较好的风格,交互用脚本语言写,然后内部调用cantrip程序

源模式
没有输入,输出只能在启动条件中控制,比如 ls

类编译器模式

没有输入也没有输出,但是报错时会发信息
,交互性较低,所以比较容易脚本化


引擎和接口分离模式

核心算法与接受用户命令,显示结果相分离

比如控制器,视图,模型

驱动/引擎组合

比如驱动可以是不同的网卡驱动,而网卡作为引擎,只有一个

客户端/服务器模式

配置者/执行者组合

比如fetchmail和fetchmailconf

fetchmailconf用于GUI 

假脱机和守护进程

守护进程,轮询作业

CLI服务器模式

其实就是守护进程负责监听,然后fork进程处理输入,比如inetd

微语言模式

GUI前端,CLI微型语言后端比如数据库软件

11.4 应用Unix接口设计模式

要促进脚本化和管道线能力(参考第 7 章),最好就是尽可能地选择最简单的接口设计模式——牵扯环境因素最少、交互最少的模式。

既有脚本方式的接口,又有GUI方式的接口

CGI 浏览器内嵌程序,用来接受用户请求,并处理

沉默原则!

喋喋不休的程序,其他程序很难理解,更难交互

程序每产生一行垃圾,用户可见的信息就少了一行。

信息内容应该符合最大惊奇原则——仅仅对偏离通常期望的情况详加说明。

12 优化

过早优化乃万恶之源

Unix的经验告诉我们最主要的就是如何知道何时不去优化。其次,最有效的优化往往是优化之外的其它事情,如:清晰干净的设计。

程序员工具箱中最强大的优化技术就是不做优化。

12.2 先估量,后优化

如果有真凭实据证明应用程序运行缓慢,这时(仅当此时)才可以考虑优

最有效的代码优化方法就是保持代码短小简单

永远不要将核心数据结构和时间关键循环抛出缓存。

“小即是美”的建议比以往更有用,尤其是考虑到核心数据结构必须留在最快的缓存里。

三种常规的策略来减少时延,(a)对可以共享启动开销的事务进行批处理,(b)允许事务重叠,和(c)缓存。

一致性在简单情况下可以保障,所以我绝对不要用二进制缓存,用map做缓存就行了

13 复杂度:尽可能简单,但别简单过了头

13.1 谈谈复杂度

Unix程序员追求简单的激情,源自注重实效的事实:复杂度就是成本

更多行的代码意味着更多的 bug,而调试常常是开发中最昂贵、最耗时的部分。

13.1.2 接口复杂度和实现复杂度的折中

Unix思想中的一个主题就是强调工具小巧锐利,设计从零开始,接口简单一致。

如果目标是抑制整体复杂度,最愿意牺牲的是什么地方?什么地方又最该被牺牲掉?

本章大多数的问题,良好品味和工程判断力要求,情况不同,则答案不同。

重要的是要培养斟酌每一个设计的习惯。

正如我们在讨论软件模块性之前的建议一样,复杂度的算盘必须打好。

偶然复杂度的产生是因为没有找到实现规定功能集合的最简方法。偶然复杂度可以由良好的设计或重新设计来去除。另一方面,选择复杂度,同某个期望的功能相关联,只能由改变工程的目标来去除。

计算资源以及人类的思考,同财富一样,不是靠储藏而是靠消费来证明其价值的。

13.3.2 折中无用

要么简约主义,要么无所不能

程序要么小巧,要么庞大,中间道路行不通

所有真正有用的程序,都想变成瑞士军刀

只有实证了其他方法行不通时,才写庞大程序

行程才是目的;顿悟在每日的实践中

第15章

从而让人心无旁骛地专注于开发中最重要(也是最享受)的部分——设计。 所以设计,是让人享受的对吗?

攀爬学习曲线的一次性付出,得到的是更有效编写程序的能力

精力也可以更多地放在设计层面而不是低层次的细节操作。

make用于文档转换,比如将html标签文件转换成易读的文本格式

看到1287

15.6 运行期调试

困难的是,语法正确的程序并不如期望的那样运行,解决办法是

透明设计性:内部数据的流向容易被人眼和简单工具审视

透明设计有利于防止bug,以及减轻运行期调试任务

牢记Unix哲学,将时间花费在设计质量上,而不是低层次的细节上,尽可能地自动化一切---包括运行期调试的细节工作

make比较出名的工具是autoconf

profiler用来工具,用来检测性能

15章看完

第16章

不言之教,无为之益,天下希及之。

不愿做不必要的工作是程序员的一大美德

避免重新发明轮子的最有效方法是借用别人的设计和实现。换句话说,重用代码

Unix的经验是,养成良好的习惯,尝试通过最少的新发明,组合现有组件以形成原型,而非匆忙地编写独立的、只能使用一次的代码。

16.2 透明性是重用的关键

实际上,任何具有非平凡API的软件,如果无法深入肌理,甚至无法正确使用。

所以,开放源码”具有更深远的意义

设计最好的实践需要情感的投入,而不是冷漠无聊的过程。软件开发者,同其它任何类型的工匠和技师一样;他们想要成为艺术家,这并不是什么私密。

16.4 

在开源世界的开发者,从来不会受最终期限的压迫,不会一闭眼、一拍脑门就发布软件。

Unix下工作,最管用的技能之一就是熟练地掌握将代码粘合在一起的各种方法,从而能够应用组合原则。

作为Unix开发者,最有价值的时间投资方法之一就是,花时间在这些站点上去了解可以获得什么东西来重用。节省下来的编码时间就是自己的 有机会去看一看 1382页面

更一般地,阅读代码是为未来而投资。可以从中学到甚多——新技术、分解问题的新方法、不同的风格和手段。使用代码和学习代码都能得到有价值的回报。

写之前先读;培养阅读代码的习惯。



第17章
好处:无需重写工具

Unix标准,POSIX,主要是描述:系统调用,C函数库,shell语义等

标准化,其实就是为了可移植

标准定义的IETF哲学:“我们反对国王、总统和投票。我们信任大致的共识和可运行的代码”

请求意见稿(英语:Request for Comments,缩写:RFC),又翻译作意见征求意见请求请求评论[1]是由互联网工程任务组(IETF)发布的一系列备忘录。

我的理解,RFC也算是一种被提出来的规范,但是流行程度取决于广大开发者

17.4 

补救糟糕的代码或设计,比起重新开始尝尝更费时费事。

Unix文化主张干脆拆毁重来。

先原型然后循环不断地测试和演进才是更好的方法。

良好的规格说明(我的理解是程序的说明文档)具有巨大的价值。

Unix开发中,文档常常在程序之前,或者至少同程序一起编写

规格说明,就是一切? 所以规格说明是?需求文档?

标准,是DNA,我们根据DNA生成RNA,RNA即是根据标准生成的系统,比如Linux

17.5 可移植性编程

可移植性,空间上,可移植到不同主机;时间上,几十年后仍然能用;时间上的可移植性甚至更重要

通常可以用autoconf来探查本地配置,从而解决可移植性问题

分离信息库和代码,我觉得和上面那个手工写的ASCII常量表有点类似

基于开放源码编程,而不是专用代码,这样能更好应对,接口迭代导致的不兼容

18 文档:向网络世界阐述代码

所见即所得(WYSIWYG)和 已标记为中心的工具(比如xml,html,和latex?) ,标记工具更容易设计格式,布局

unix手册,一般都有个BUGS部分,事无巨细地揭露软件的已知缺陷

文档类型定义(DTD,Document Type Definition)是一种特殊文档,和XML语法规则相关

看完

19章 

软件和性一样,越自由越好

数量多不会被认为是质量高。尤其是,决不要因为害怕别人看不懂而省略功能细节,决不要为了面子而不对存在的问题提出警示。不愿坦露问题才会损害信誉和用户,坦白了的问题则不会。

开源开发利用了这样的事实,甄别和修改 bug 的任务适合分解成多个并行的子任务——这和实现某个特殊算法不一样

尽早发布,经常发布

因此,精工细作,等一切完美了才发布的想法是要不得的。

努力选择唯一且容易键入的名称前缀

使用GNU自动工具

先测试再发布代码


发布前对代码进行健全检查
“健全检查(sanity check)”的意思是:使用可以获得的每一款工具来检查每个人类易犯的错误。使用工具捕捉到的错误越多,用户和自己需要对付的就越少。

比如,使用查找内存泄漏和运行期错误的软件;Electric Fence和Valgrind

拥有FAQ文件可以让你省很多力气。当关于项目的某个问题经常出现时,就加到FAQ文件中

发展良好的FAQ可以减少项目维护者一个数量级的负担,甚至更多。

看完了

20 未来:危机与机遇(看完)

预测未来的最好方法就是去创造未来 

Unix 程序员在 30年风雨中学到最有经验的回应,就是回到最初的准则——优先从流、命名空间、进程等Unix基本抽象中得到更多效用,而不是增加新的东西。

更优秀解决方案的最危险敌人,就是一个现存的、足够优秀的代码库。

C语言缺乏抛出附带数据的命名异常的机制。因此,Unix API中的C函数用与众不同的的返回值(通常是-1或NULL)并设置全局变量errno来报告错误。[7]

交互设计也包含了大量坚实的真理,需要为每个Unix程序员所知道。

ioctl和fnctl比较尴尬

通过fcntl设置的都是当前进程如何访问设备或文件的访问控制属性,例如读、写、追加、非阻塞、加锁等,但并不设置文件或设备本身的属性,例如文件的读写权限、串口波特率等。

ioctl函数用于设置某些设备本身的属性,例如串口波特率、终端窗口大小,注意区分这两个函数的作用。

波特率即指一个单位时间内传输符号的个数。

无名师的Unix心转

现在得到的90%,比等不来的100%更有价值,他强调实现的健壮性和简单性

Unix传统是简单和空

读者评论

一个人所拥有的的理念和素质,远比他所表现出来的专业技能重要的多?????  我不赞成,有一说一

你可能感兴趣的:(unix,服务器)