The Art of Unix Programming - jwyx/ForFun GitHub Wiki

Unix哲学

一个程序只做一件事,并做好
程序要能协作
程序要能处理文本流,因为这是最通用的接口

Unix之得

Open-Source Software
    peer-review-intensive development of freely shared source code
Cross-Platform Portability and Open Standards
    Unix API可以作为编写真正可移植软件的硬件无关标准
Internet & World Wide Web
Open source community
Flexibility All the Way Down

Unix philosophy

1. Rule of Modularity: Write simple parts connected by clean interface
    Brian Kernighan "计算机编程的本质就是控制复杂度"
    1) 封装:
        封装良好 = 不会过多向外披露自身细节,不会直接调用其他模块的实现码,也不会胡乱共享全局数据
        模块之间通过API - 一组严密,定义良好的程序调用和数据结构来通信

        API:
            实现层面: 作为模块之间的滞涩点(choke point),阻止各自的内部细节被相邻模块知晓
            设计层面: 正是API真正定义了整个系统
        
        如何验证API设计良好:
            养成在编码前为API编写一段非正式书面描述的习惯
            定义接口
            编写简要注释
            编写代码
            制作roadmap document
        
        软件系统应设计成由层次分明的嵌套模块组成,而且每个层面上的模块粒度应降至最低。

        - 紧凑性
            就是一个设计是否能装进入大脑的特性;
            测试软件紧凑性的一个很实用的好方法:
                有经验的用户通常需要操作手册吗? 如果不需要,那么这个设计(或者至少这个设计的涵盖正常用途的子集)就是紧凑的
          P 88

        - 正交性

2. Rule of Clarity: Clarity is better than cleverness
    保证代码的可维护性,进行代码注释,选择清晰和可扩展的算法和实现

3. Rule of Composition: Design programs to be connected to other programs
    在输入输出方面,Unix提倡采用简单,文本化,面向流,设备无关的格式
    Unix中,文本流之于工具 == 消息之于对象
    文本流界面的简洁性加强了工具的封装性
    要想让程序具有组合性,就要使程序彼此独立

4. Rule of Separation: Separate policy from mechanism; separate interface from engines
    1) 将应用按照一个库来编写,这个库包含许多由内嵌脚本语言驱动的C服务程序,而至于整个应用的控制流程则用脚本撰写
    E.g. Emacs,使用内嵌的Lisp解释器来控制用C编写的编辑原语操作
    2) 将应用分成协作的前端和后端进程,通过套接字上层的专用应用协议进行通讯;前端实现策略,后端实现机制

5. Rule of Simplicity: Design for simplicity; add complexity only where you must
    鼓励简洁为美,总是设法将程序系统分解为几个能够协作的小部分,并本能地地址任何用过多噱头来粉饰程序的企图

6. Rule of Parsimony: Write a big program only when it is clear by demonstration that nothing else will do
    “大”有两重含义:体积大,复杂程度高

7. Rule of Transparency: Design for visibility to make inspection and debugging easier
    调试通常会占用3/4甚至更多的开发时间,减少调试工作量的方法就是设计时充分考虑transparency和discoverability
    - Transparency:    一眼能看出软件是在做什么以及怎么做的
    - Discoverability: 程序带有监视和显示内部状态的功能
    调试选项的设置: 应该尽量在设计之初就考虑:不但能展示其正确性,也能把开发者解决问题的思维模型告诉后来者
    使用足够简单的输入输出格式: 方便展示其正确性,并保证能很容易地检验有效输入和正确输出之间的关系是否正确
    接口简洁: 方便其他程序对其进行操作 - 测试监视工具 和 调试脚本

8. Rule of Robustness: Robustness is the child of transparency and simplicity
    perform well under unexpected conditions as well as normal conditions
    设计时要考虑到能承受极端大量的输入
    避免在代码中出现特例
    让程序健壮的方法: 就是让程序的内部逻辑更易于理解,以下两种方法:
    - transparency: 一眼就能够看出时怎么回事
    - simplicity:   不需要绞尽脑汁推算出所有可能的情况
    => 模块性(simple parts, clean interfaces) 是组织程序以达到更简洁目的的一个方法

9. Rule of Representation: Fold knowledge into data, so program logic can be stupid and robust
    数据要比编程逻辑更容易驾驭
    编程的核心是数据结构,而不是算法;数据压倒一切

10. Rule of Least Surprise: In interface design, always do the least surprising thing
    最易用的程序就是最切合用户已有知识的程序 (感觉也是交互设计的原则)
    学会使用惯例,可以缓和学习曲线 (莫非是Rails哲学的原因??)
    Note: 最好让不同事物有明显区别,防止用户产生错误的假设

11. Rule of Silence: When a program has nothing surprising to say, it should say nothing
    Unix最古老最持久的设计原则之一
    行为良好的程序应该默默工作,Silence is golden.
    只显示重要信息!
    设计良好的程序将用户的注意力视为有限的宝贵资源,只有在必要时才要求使用

12. Rule of Repair: Repair what you can - but when you must fail, fail nosily and as soon as possible
    出现异常时,马上退出并给出足量的错误信息
    最理想的情况当然是软件能够适应和应付各种错误输入和自身的运行错误,但是如果做不到,则让程序尽可能以容易诊断错误的方式终止
    Postel's Prescription: "be liberal in what you accept, and conservative in what you send"
    - make as much sense as they can from ill-formed inputs
    - either fail noisily or pass strictly clean and correct data to the next program in the chain
    To design for generosity rather than compensating for inadequate standards with permissive implementations

13. Rule of Economy: Programmer time is expensive; conserve it in preference to machine time
    1) 大多数应用场合都应该使用高一级的语言, Perl, Tcl, Python, Java, Lisp, 甚至shell
    2) 教会机器如何做更多低层次的编程工作

14. Rule of Generation: Avoid hand-hacking; write programs to write programs when you can
    人类很不擅长干辛苦的细节工作,程序规格越简单越抽象,设计者就越容易做对
    使用代码生成器是易于出错的细节工作自动化,e.g. Parser/Lexer, makefile生成器, GUI interface builder

15. Rule of Optimization: Prototype before polishing. Get it working before you optimize it
    过早优化是万恶之源
    做好prototype可以帮助你避免为蝇头小利而投入过多的时间
    不知道瓶颈所在就匆忙进行优化,这可能是唯一一个比乱加功能更损害设计的错误
    过早的局部优化会降低整体性能
    传统! 先制作原型,再精雕细琢。优化之前先确保能用。
    极限编程: 先求运行,再求正确,最后求快!
    借助原型化找出哪些功能不必实现,有助于对性能进行优化;那些不用写的代码显然无需优化。目前最强大的优化工具,恐怕就是delete

16. Rule of Diversity: Distrust all claims for "one true way"
    Unix从不相信所谓的“不二法门”;奉行的是广泛采用多种语言,开放的可扩展系统和用户定制机制

17. Rule of Extensibility: Design for the future, because it will be here sooner than you think
    为数据格式和代码留下扩展的空间
    设计协议或是文件格式时,应使其具有充分的自描述性以便可以扩展
    程序接合部要灵活,在代码重加入“如果你需要......”的注释
    稍微增加一点让数据部署具有自描述性的开销,就可以在无需破坏整体的情况下进行扩展?? 如何实现??

Unix哲学总结! KISS = Keep It Simple, Stupid! 保持简单质朴!!

Attitude matters too:
    看到该做的就去做;如果不确定什么是对的,那么就只做最少量的工作,确保任务完成就行,至少直到明白什么是对的。

要良好地运用Unix哲学,你应该:
    珍惜时间
    不要因为蛮干重造轮子
    多用巧劲
    善用工具,尽可能将一切都自动化

History

Those who cannot remember the past are condemned to repeat it. -- George Santayana
忘记过去的人,注定要重蹈覆辙。前事不忘,后事之师。

small experimental prototype

"second-system effect":
    The urge to add everything that was left out the first time around all too frequently leads to
    huge and overcomplicated design.
"third-system effect":
    Sometime, after the second system has collapsed of its own weight, there is a chance to go back to
    simplicity and get it really right.
协作式开发和源码共享是Unix程序员的法宝

Unix的历史中,最大的规律就是,距开源越近就越繁荣。另一个教训就是,别和低价而灵活的方案较劲。

Unix哲学同其他哲学的比较

Unix操作系统设计,以及由此发展出的编程设计哲学产生的联系。

The Operating System's Unifying Idea:
    Unix has a couple of unifying ideas or metaphors that shape its APIs and the development style that proceeds from them. 
    E.g. "everything is a file" model and the pipe metaphor

任何特定操作系统的开发风格均受到系统设计者灌输其中的统一性理念的强烈影响 -- 由系统工具和API塑造的模型将渗透到应用编程中

多任务能力:
   single-tasking
   cooperative multitasking: 支持多进程,但是一个进程运行前必须等待前一个进程主动放弃占用CPU
   preemptive multitasking:  time slice由调度程序来分配,这个调度程序定期中断或抢断正在运行的进程而把控制权交给下一个进程
   多任务 vs. 多用户:
       不同! 一个操作系统可以是多任务处理但是只支持单用户,这种情况下,计算机支持的是单个控制台和多个后台进程。
       真正的多用户支持需要多个用户权限域。

在Unix中,低价的进程生成和简便的进程间通讯(IPC Inter-Process Communication)使众多小工具,管道和过滤器组成一个均衡的系统称为可能。

管道和所有其他经典的Unix IPC方法有一个精妙的性质,就是要求把程序间的通讯简化到某一程度而促使功能分离。
Unix具有灵活的IPC和使用IPC的强大传统!!

内部边界:
    Unix准绳是: 程序员最清楚一切。当你对自己的数据进行危险操作时,Unix并不阻止你;却小心避免你才在别人的数据上。
    Unix提倡设立多个帐号,每个帐号具有专属,可能不同的权限,以保护用户不受行为不端的程序的侵害。

Unix设置了至少三层内部边界来防止恶意用户或者有缺陷的程序:
    1. 内存管理
        Unix用硬件自身的内存管理单元(MMU)来保证各自的进程不会侵入到其他进程的内存地址空间
    2. 多用户设置的真正权限组
        普通用户的进程未经允许,就不能更改或者读取其他用户的文件
    3. 把涉及关键安全性的功能限制在尽可能小的可信代码快上

Unix文件既没有记录结构(record structure),也没有文件属性。??

不使用二进制文件格式存放关键数据

CLI vs. GUI 作为一般表现模式的选择,将影响设计的许多方面 - 从进度调度,内存管理直到应用程序使用的API

目标受众:
    Client: 轻量,只支持单个用户,能够在小型机器上运行,随需开关机器,没有抢先式多任务处理,为低延迟作了优化,大量资源都用于花哨的用户界面
    Server: 重量,能够连续运行,为吞吐量优化,完全抢占式多任务处理以处理多重会话
    
    Unix认为用户本身更懂干什么  

开发门坎:
    Unix下,廉价工具和简单接口支持的是轻松编程,玩家文化和开拓探索。