跳至正文

Unix编程艺术 读书笔记

1 哲学

  • 性能—时间的指数曲线对软件开发过程所引发的结果,就是每过18个月,就有一半的知识会过时。Unix并不承诺让你免遭此劫,只是让你的知识投资更趋稳定
  • 策略相对短寿,而机制才会长存
  • 对于程序员和开发人员来说,如果完成某项任务所需要付出的努力对他们是个挑战却又恰好还在力所能及的范围内,他们就会觉得很有乐趣。
  • 那些毫无动力、松松垮垮而且薪水微薄的程序员们,能在短短期限内,如同神灵附体般造出稳定而新颖的软件——这只不过是经理人永远的梦呓罢了。
  • 让每个程序就做好一件事。如果有新任务,就重新开始,不要往原程序中加入新功能而搞得复杂。
  • Unix哲学是这样的:一个程序只做一件事,并做好。程序要能协作。程序要能处理文本流,因为这是最通用的接口。
  • 你无法断定程序会在什么地方耗费运行时间。瓶颈经常出现在想不到的地方,所以别急于胡乱找个地方改代码,除非你已经证实那儿就是瓶颈所在。
  • 估量。在你没对代码进行估量,特别是没找到最耗时的那部分之前,别去优化速度。

    所以实际投入使用的排序算法,都是分层的,在n小时用冒泡,复杂的时候才考虑快排或者并归。

  • 花哨的算法在n 很小时通常很慢,而n通常很小。花哨算法的常数复杂度很大。除非你确定n总是很大,否则不要用花哨算法(即使n很大,也优先考虑原则2)。
  • 其中一种压力就是来自技术上的虚荣心理。
  • 在设计中,你应该主动将代码的复杂度转移到数据之中去。
  • 还不知道瓶颈所在就匆忙进行优化,这可能是唯一一个比乱加功能更损害设计的错误。
  • 先制作原型,再精雕细琢。优化之前先确保能用
  • 如果可能,用C编写前,先用解释性语言搭建原型
  • 看到该做的就去做——短期来看似乎是多做了,但从长期来看,这才是最佳捷径。
  • 一旦某人已经解决了某个问题,就直接拿来利用,不要让骄傲或偏见拽住你又去重做一遍。永远不要蛮干;要多用巧劲,省下力气到需要的时候再用,好钢用在刀刃上。善用工具,尽可能将一切都自动化。
  • 软件设计和实现应该是一门充满快乐的艺术,一种高水平的游戏。

2 历史——双流记

现代操作系统虽然很复杂,但确实也就是一个分时系统,在任何时间段让程序独立的运行而已。

  • 如果有足够多眼睛的关注,所有的bug都无处藏身
  • 距开源越近就越繁荣。任何将Unix专有化的企图,只能陷入停滞和衰败。

3 对比:Unix哲学同其他哲学的比较

  • Unix系统拥有抢先式多任务(preemptive multitasking)能力。在Unix中,时间片由调度程序来分配,这个调度程序定期中断或抢断正在运行的进程而把控制权交给下一个进程。几乎所有的现代操作系统都支持抢占
  • 多用户的概念除了多进程还需要一套权限隔离的系统来维护。
  • 因为Windows没有处理好程序库的版本控制问题,所以长期备受被称为“DLL地狱(DLL hell)”配置问题的折磨,在这个问题中,安装新程序可以任意升级(或降级)现有程序运行依赖的库文件。专用的应用程序库和厂商提供的系统库都存在这个问题:应用程序和特定版本的系统库一起发布非常普遍,一旦没有特定的系统库,应用程序就会无声无息地垮掉。
  • MVS的统一性理念是:一切皆批处理。

4 模块性:保持清晰,保持简洁

  • 软件设计有两种方式:一种是设计得极为简洁,没有看得到的缺陷;另一种是设计得极为复杂,有缺陷也看不出来。第一种方式的难度要大得多
  • 一些最有能力的开发者,一开始总是定义接口,然后编写简要注释,对其进行描述,最后才编写代码——因为编写注释的过程就阐明了代码必须达到的目的。
  • 假设其它所有因素(如程序员能力)都相同,200 到 400 之间逻辑行的代码是“最佳点”,可能的缺陷密度达到最小。这个大小与所使用的语言无关——这个结论有力支持了本书中其它地方提出的建议
  • 紧凑性就是一个设计是否能装进人脑中的特性。
  • C++是反紧凑性的——该语言的设计者已经承认,他根本不指望有哪个程序员能够完全理解C++。
  • 合理对待紧凑性,设计中尽量考虑,决不随意抛弃。

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

  • 当你很想设计一个复杂的二进制文件格式,或一个复杂的二进制应用协议时,通常,明智的做法是躺下来等待这种感觉过去。
  • 使用二进制协议的唯一正当理由是:如果要处理大批量的数据集,因而确实关注能否在介质上获得最大位密度,或是非常关心将数据转化为芯片核心结构所必须的时间或指令开销。
  • 这件事很浅显很正确,却是违反直觉的。设计一个精良的二进制结构存储效率可能不如明文+压缩。

    创建一个简单的工具来做好压缩,要比仅对文件某些部分进行特别压缩更有效,原因在于,压缩工具可以扫描所有数据,然后找到信息中的所有重复部分进行压缩。

  • 创建一个简单的工具来做好压缩,要比仅对文件某些部分进行特别压缩更有效,原因在于,压缩工具可以扫描所有数据,然后找到信息中的所有重复部分进行压缩。
  • 因为网络带宽要比存储昂贵得多,所以需更加重视事务处理的经济性。但如今的宽带费用都很低了。

6 透明性:来点儿光

  • 透明性和可显性对用户和软件开发人员都很重要。但是重要性体现在不同的方面。用户喜欢 UI 中的这些特性,是因为这意味着学习曲线比较平缓。
  • 这个说法我好喜欢,优雅的代码除了一种自我实现感还有很重要很基础的一点是程序就是面向代码的。

    软件开发者喜欢代码本身(用户不可见部分)的这些品质,因为他们经常需要对代码有很好理解后才能进行修改和调试。

  • 软件开发者喜欢代码本身(用户不可见部分)的这些品质,因为他们经常需要对代码有很好理解后才能进行修改和调试。
  • GCC由一系列处理阶段组成,并由一个驱动程序将其紧密结合在一起。它们是:预处理器、解析器、代码生成器、汇编器和链接器。
  • 要追求代码的透明,最有效的方法很简单,就是不要在具体操作的代码上叠放太多的抽象层。
  • Unix 程序员学到了一种品性,就是宁愿抛弃、重建代码也不愿修补那些蹩脚的代码

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

  • 总的来说,线程不是降低而是提高了全局复杂度,因此,除非万不得已,尽量避免使用线程。
    -这本书真的好反感使用线程啊,不过想想确实也有合理性。

    通常,你可以发现避免使用线程是可能的。

8 微型语言:寻找歌唱的乐符

  • 对软件错误模式进行的大量研究得出的一个最一致的结论是,程序员每百行代码出错率和所使用的编程语言在很大程度上无关。[1]更高级的语言可以用更少的行数完成更多的任务,也意味着更少的bug。
  • 美是抵御复杂的最后武器。

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

数据比程序逻辑更易驾驭。

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

-如果有绝对最优解,就不必让用户选择

首先,对于能够可靠地进行自动检测的东西,就不要提供配置开关。

  • 首先,对于能够可靠地进行自动检测的东西,就不要提供配置开关。

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

  • 我们将使用五种度量标准对接口风格进行分类:简洁、表现力、易用、透明和脚本化能力。
  • 当人们说一个用户接口是直观的,他们的意思是(a)它是可显的,(b)用法是透明的,(c)遵循最小立异原则
  • 在Unix传统中,已经形成了良好的接口设计模式,可以完成以上讨论的权衡。
  • 理由三:垃圾信息是对用户带宽的无谓消耗。在屏幕上,这又增加了一个分心的来源,往往让人们不得不在处理更重要的前台工作(例如同他人的交流)的同时耗费心力。

12 优化

  • 通常,指令加载要比执行花费的时间更多。
  • 例如,在3D图形引擎中,优化旋转操作的sin(x)函数表在现代机器中占据365×4字节的空间。在处理器缓冲速度没有内存查询快时,这显然是个速度优化。但是现在,比起函数表产生的附加缓存击不中的可能开销,每次重新计算可能更快。
  • 实际上,经验法则是尽可能低的时延设计,和忽略带宽成本

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

  • Unix程序员已学到了一种世界观:简单即美即雅即善,而复杂即丑即怪即恶。
    Gabriel 在更关注接口简单性的“MIT”哲学和更重视实现简单性的“New Jersey”哲学之间进行了比较,然后提出,尽管MIT哲学能够引导软件在抽象上做到更好,但New Jersey模型(差者)更具传播特质。
    -原来很多库接口的设计还有这样的考虑

    在MIT哲学中,应该暂停系统调用,伺中断处理完成之后自动恢复之——这较难实现,但接口更为简单;而在New Jersey哲学下,系统调用会返回一个错误表明已被中断,用户必须重新执行——这实现起来非常简单,但编程接口却较难使用。

  • 他所采用的简单实现就是允许“404:Not Found”可以作为一个响应,这使得万维网非常轻便,并获得了广泛的传播和巨大的成功。
  • 重点划一下

    在理想世界,Unix程序员只愿意手工打造小巧完美的软件宝石,每个都那么小巧、那么优雅、那么完美。然而现实中很不幸的是,太多复杂问题需要复杂的解决方案。仅仅十行的程序,再优雅也无法控制喷气客机。

  • 计算资源以及人类的思考,同财富一样,不是靠储藏而是靠消费来证明其价值的
  • 作为vim的信徒必须捍卫,你凭啥这么说!!

    比较而言,vi 看起来相当臃肿而不紧凑。

14 语言:C还是非C

  • 我语言的极限便是我世界的极限。
  • 这一点确实如此,用C/C++高效但开发维护成本高,但很多时候主要的消耗还是在io和网络

    使用脚本语言的性能损失对真实世界的程序来说经常微不足道,因为真实世界的程序往往受I/O事件等待、网络延迟以及缓存列填充等限制,而非CPU的自身效率。

  • 使用脚本语言的性能损失对真实世界的程序来说经常微不足道,因为真实世界的程序往往受I/O事件等待、网络延迟以及缓存列填充等限制,而非CPU的自身效率。
  • 非常非常认同,因为开发的本质是硬件设备,所有的高级语言虽然屏蔽了硬件体系,但如果要优化还是逃不开的

    即使更高级的语言能够满足编程的要求,我们仍然要学习C,其中一个充分理由就是C能帮助我们学会在硬件体系层次上考虑问题。

  • 即使更高级的语言能够满足编程的要求,我们仍然要学习C,其中一个充分理由就是C能帮助我们学会在硬件体系层次上考虑问题。
  • 总结:C 语言最佳之处是资源效率和接近机器语言。而最糟糕的地方是其编程简直就是资源管理的炼狱。
  • 总结:C++的最佳之处是编译效率以及面向对象和泛型编程的结合。最糟之处是它非常怪异复杂,往往鼓励过分复杂的设计。
  • Java编程语言的设计目标是“write once,run anywhere(一次编写,到处运行)”

15 工具:开发的战术

  • 作为通用法则,程序90%的执行时间都耗费在10%的代码上。

19 开放源码:在Unix新社区中编程

  • 多数人在发布自己的项目之前,都是从修补他人的软件而开始接触开源软件开发的。
  • 如果档案文件都是类似GNU风格的名称,主干前缀全小写且只包含字母和数字,后接连字号,后再接版本号、扩展和其它后缀,对大家都有帮助。

20 未来:危机与机遇

  • Plan 9还有许多其它值得推荐的地方,包括一些问题重重Unix系统调用接口的再造、摒弃了超级用户概念的,以及许多其它有趣的再思考。它的血统没有污点,设计优雅,而且揭露了Unix设计中的一些严重错误。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

目录