前言
Unix是開源運動歷史上最著名的代表之一,而如今前端是開源運動中最活躍的一支隊伍,我相信它們兩者之間一定有共通之處。讀懂此書,可以讓我們以后看待軟件開發(fā)時角度變得不那么狹隘。
內容
前面三章精要的概括了Unix的哲學思想、歷史發(fā)展、以及和其他操作系統(tǒng)的對比。
第一章:哲學
第一章開門見山, 鞭辟入里地詮釋了Unix的靈魂所在:一門技術的編程藝術和設計哲學。
為什么它的生命力如此長久?作者列舉了兩個原因:一是伴生的C語言的龐大影響力,二是帶目錄節(jié)點的樹形文件名字空間和用于通信的管道機制。
支持者認為Unix有什么優(yōu)點呢?主要如下:
- 開源軟件,同僚復審
- 跨平臺可移植性和開放標準
- 與Internet的融合
- 開源社區(qū)
- 簡潔靈活
- 讓編程變得有趣
- 設計思想經(jīng)典,廣泛被借鑒
這些優(yōu)點同樣值得前端項目借鑒,大家想想jquery、vue、react、angular等是不是或多或少地符合以上幾點?
當然Unix也不全是優(yōu)點,它在商業(yè)上的不成功和追隨者的過于狂熱成了反對者的詬病之處。
另外在設計理念上,由機制而不是策略主導設計,認為最終用戶比設計人員更清楚自己想要什么也導致了一些問題。不過,時間證明,這才是保持生命力長久的秘訣。
至于具體優(yōu)點有哪些,簡直是羅列不過來,四個大前提,五個原則,十七點小的概況。不過總的來說就是開發(fā)人員需要分清輕重緩急、懷疑一切,并以幽默樂觀的態(tài)度面對一切。一言蔽之:Keep It Simple,Stupid。
本章的最后描述了這些設計哲學是如何應用在Unix中的,我們前端同樣也可以應用,比如著名的前后端分離思想,協(xié)議文本化(http/json)等。
- 依賴轉置
- 數(shù)據(jù)流文本化
- 數(shù)據(jù)庫和應用協(xié)議文本化
- 前后端分離
- 盡可能先寫原型
- 恰當混用編程語言
- 寬收嚴發(fā)
- 不需要丟的信息絕對不丟
- 小就是美
- 追求軟件設計的卓越化
第二章:歷史
Unix的發(fā)展,作者使用了三重境界來描述,和王國維的“昨夜西風凋碧樹。獨上高樓,望盡天涯路。衣帶漸寬終不悔,為伊消得人憔悴。眾里尋他千百度,驀然回首,那人卻在,燈火闌珊處?!比鼐辰珙H為相似,值得深思。
簡單評價一下Unix的歷史 :起于兼容分時系統(tǒng),以簡單好用走遍江湖,廣納各高校貢獻,雖失勢于商業(yè),終借Linux重獲新生,涅槃騰飛。
那推動Unix發(fā)展的英雄們都是誰呢?自然是hacker們,他們是計算機世界的江湖和俠客,雖然在社會和高校中各成一派,不過在經(jīng)歷過互聯(lián)網(wǎng)大融合(Unix和TCP/IP、ARPANET融合)和自由軟件運動后Unix文化已經(jīng)上升到一種意識形態(tài)的層面,協(xié)同開發(fā)也成為一種趨勢,分權、公開、同僚復審的特點在其中體現(xiàn)的淋漓盡致,最終導致了開源運動的興起。
老派Unix陣營在商業(yè)上是吃過虧的,而開源運動以一種更親和市場、更少對抗性的方式把軟件介紹給外部世界,從而也彌補了商業(yè)上的不足。
可以這樣說,軟件離開源越近越繁榮,保持靈活性,別和低價而靈活的方案較勁,就可以得到長久而旺盛的發(fā)展。
第三章:對比
這一章作者對比了Unix和其他操作系統(tǒng)的設計和編程習俗,雖然其中舉的很多例子已經(jīng)隨風而逝了,但是其核心理念的對比還是非常具有參考價值的。
首先,作者對操作系統(tǒng)的風格元素進行了對比, 因為它反映了操作系統(tǒng)設計者的意圖,體現(xiàn)了成本和編程環(huán)境的限制對設計的均衡影響,更重要的是這種文化會隨機漂移,影響其他軟件。
具體又從下面幾個角度進行詳細說明:
- 統(tǒng)一性理念
Unix一切皆文件以及管道概念。
- 多任務能力
即多進程并發(fā)能力,Unix有搶占式多任務能力。
其他操作系統(tǒng)有協(xié)作式多任務能力。
- 協(xié)作進程
Unix具有低價的進程生成成本、簡便的IPC、以及能組合各種管道過濾器小工具,這就避免了多線程的坑,使系統(tǒng)的各個部分容易合作。
- 內部邊界
以程序員最清楚一切作為準繩,從而采取多用戶控制權限,在系統(tǒng)內部設置三層內部邊界:內存管理、多用戶權限組、涉及安全的可信代碼塊。
- 文件屬性和記錄結構
Unix沒有,因為覺得記錄結構是一種雞肋的存在。文件屬性可以幫助理解文件,但在面向字節(jié)流工具和管道的世界中會有語義問題。
- 二進制文件格式
Unix采用文本格式,而二進制的兼容性和靈活性不好。
7.首選界面風格
Unix采用CLI風格,而其他操作系統(tǒng)有GUI風格,雖然GUI或者CLI沒做好都有問題,但CLI更易于程序員得到自己想要的東西。
- 目標受眾
Unix為技術用戶而設計,其他操作系統(tǒng)有為服務端、客戶端、最終用戶、單機、聯(lián)網(wǎng)等受眾設計的。
- 開發(fā)的門檻
開發(fā)門檻是由開發(fā)工具的金錢成本、成為熟練開發(fā)者的時間成本、甚至文化門檻組成,Unix支持輕松編程、玩家文化和精英編程文化。
- 具體操作系統(tǒng)比較
- VMS大全腫,商業(yè)領域尚可容忍
- MacOS的統(tǒng)一性理念是Mac界面方針,邊界脆弱,MacOS X融合Unix特點
- OS/2單用戶系統(tǒng),不靈活
- Windows辣雞但勝在商業(yè)
- BeOS深入線程化、專攻大量數(shù)據(jù)處理,死在商業(yè)
- MVS死板,用于大型機
- VM/CMS是Unix祖先,在大型機領域如魚得水
- Linux繼承Unix思想,更進一步連接世界和人
最后作者總結,種瓜得瓜,種豆得豆,一個好的操作系統(tǒng)必須可移植、支持網(wǎng)絡、把握客戶端的力量。從這里我突發(fā)奇想,會不會操作系統(tǒng)的未來就是瀏覽器呢?
設計思想
從第四章到第十三章都是展開描述Unix各個設計思想的精髓,配有大量的例子,雖然這些例子大部分已經(jīng)湮沒在歷史的長河里,不過對當今的軟件開發(fā)仍然具有重要借鑒意義。
第四章:模塊性(保持清晰,保持簡潔)
隨著代碼的復雜度日益增加,我們需要使用子程序、庫、進程等來劃分代碼。比如可以用定義清晰的接口把若干簡單模塊組合起來,這樣把程序劃分開來,并且讓它們易于協(xié)作,這是良好的設計方式。好的軟件滿足模塊化、正交性、緊湊性三個特點。
而模塊化最突出的特點就是封裝。模塊間通過一組嚴密、定義良好的程序調用和數(shù)據(jù)結構來進行通信。
有幾點需要注意:
- API是實現(xiàn)和設計間的滯塞點
- 簡潔清晰的API表明設計是良好的
- 模塊不宜過多也不宜多少
模塊化另一處特點是緊湊性和正交性,一個軟件,設計和人類使用習慣相符就是緊湊的,任何操作沒有副作用就是正交的。
但緊湊意味著精煉,實際上的軟件基本上是半緊湊型的,因為如果編程者需要記憶的條目數(shù)大于七說明API不是緊湊型的。比如C和Python是半緊湊的、C++是反緊湊的,造成這種現(xiàn)象的原因是有時候為了性能和適應范圍等要犧牲緊湊性。
正交性代碼在源頭上減少了bug的可能性,更容易文檔化和復用,重構根本的目標就是提高正交性。
實踐當中如何體現(xiàn)緊湊型和正交性呢?作者告訴我們需要遵循SPOT原則、圍繞強單一中心、時刻考慮分離的價值。
SPOT原則即真理的單點性,比如通過重構去除重復代碼保留真理:
- 去除重復數(shù)據(jù)->讓代碼生成程序
- 去除重復代碼知識點->讓代碼生成部分文檔
- 去除重復頭文件和接口聲明->讓代碼生成頭文件和接口聲明
- 讓數(shù)據(jù)結構“無垃圾,無混淆”
強單一中心即圍繞“解決一個定義明確的問題”設計程序,例如diff算法、grep模式匹配、yacc生成語法解析器等都是專門為了解決某個問題才出現(xiàn)的。
分離的價值指從零開始,先去想盡量少的能做的事情,有點類似前端的漸進增強思想。
在進行模塊化之前,我們還需要考慮一個問題:那就是軟件的層次。
大體可以分為自頂向下和自底向上的層次,自頂向下適用于精確、穩(wěn)定、底層自由的軟件,自底向上適用于探索性的編程,同時拋棄的代碼也比另外一種少。我們需要根據(jù)情況去設計,要么抽象化細節(jié),要么圍繞某個模型組織代碼,而實際情況往往是結合兩種方式設計程序,這其中需要膠合層用來協(xié)調。
但膠合層要盡量薄,可以看做是分離原則的升華,C語言就是一個很好的膠合層。
膠合層的應用之一就是各個共享庫。雖然理論上膠合層不應存在,只有多變的策略和不變的機制。
另外作者討論了OO的思想,他指出OO只在某些領域適用,Unix程序員們大多是持懷疑態(tài)度的,因為堆砌抽象層是很累人的事情,容易陷入過度分層,而且過早設計喪失了優(yōu)化的機會,以至于說:“過早優(yōu)化就是萬惡之源?!?/p>
這章的最后,作者設計了幾個問題供讀者自測,看自己的代碼是否遵循了模塊化的原則:
- 全局變量有多少?
- 單個模塊的大小是否合適?
- 模塊內的單個函數(shù)是否太大?
- 代碼是否有內部API?
- API的入口是否超過七個?
- 模塊的入口點分布怎么樣?
第五章:文本化(好協(xié)議產(chǎn)生好實踐)
上一章講了如何把程序拆分成若干個模塊,這章就關于這些模塊間如何通信給出了答案。比如用于協(xié)作通信的應用協(xié)議如何設計,當然還有一種文本化的應用場景:數(shù)據(jù)存儲,也會提到。
- 為了便于數(shù)據(jù)傳輸,通常需要序列化(列集)和反序列化(散集),比如前端中JSON的parse和stringify方法。
- 設計協(xié)議時需要考慮互用性、透明性、可拓展性和經(jīng)濟性。
- 性能不需要最先考慮,不然可能會是一種過早優(yōu)化。
- 數(shù)據(jù)文件和控制文件的信息流方向是不一樣的。
文本化最重要的特點是透明、可拓展。
比如Unix口令格式就是犧牲性能換透明和可拓展性的例子,.newsrc格式同樣是舍經(jīng)濟性而取透明性和可操作性。但也有適用于二進制的情景,比如PNG格式。
隨后作者又道出了數(shù)據(jù)存儲文本化非常重要的一個特點:擁有一套句法和詞法約定的數(shù)據(jù)文件元格式。具體給了幾個例子,JSON是我加上去的。
| 格式名 | 特點 |
|---|---|
| DSV(分隔符分隔值) | 使用冒號分隔值,通過反斜杠轉義符處理特殊情況,和CSV形成鮮明對比 |
| RFC 822 | 使用Tab或者Space來延續(xù),空行解釋為結束。應用于郵件和HTTP協(xié)議,有多個記錄的時候邊界可能不明顯 |
| Cookie-Jar | fortune使用的格式,使用%分隔,適用于結構不易區(qū)別的文本段 |
| Record-Jar | 前兩個格式的混合體,使用%%\n分隔,適用于可變字段的集合 |
| XML | 類似于HTML、需要專門的解析器,適用于復雜的問題(比如遞歸嵌套) |
| Windows INI | 使用名稱-屬性對分隔,比較雞肋,復雜數(shù)據(jù)不及XML,簡單不如DSV |
| JSON | 使用鍵值對分隔,如今比XML流行 |
那Unix對文本化格式的約定是什么樣的呢?
- 如果可能,以新行符結束的每一行只存一個記錄。
- 如果可能,每行不超過80個字符。
- 使用#引入注釋。
- 支持反斜杠轉義約定。
- 在每行一條記錄的格式中,使用冒號或者任何連續(xù)的空白作為分隔符。
- 不要糾結tab和space。
- 優(yōu)先使用十六進制而不是八進制。
- 對于復雜的記錄,使用“節(jié)”,如果有多行,使用%%\n等作為分隔符。
- 節(jié)格式中,支持連續(xù)行。每行一個記錄字段或者用冒號終止字段名關鍵字作為引導字段。
- 格式可以自描述,不然就設立一個版本號。
- 注意浮點數(shù)取整問題。
- 不要壓縮一部分。
前面花了這么多力氣描述數(shù)據(jù)存儲文本化,當然是給描述協(xié)議文本化做鋪墊。因為適用于數(shù)據(jù)文件格式的好處同樣也適用于協(xié)議。
協(xié)議設計另外需要注意的點就是要牢記端對端設計原則,包括安全、認證、性能。
同樣舉了例子,關于郵件協(xié)議的:
- SMTP發(fā)郵件,設計良好。
- POP3收郵件,和SMTP如出一轍。
- IMAP收郵件,希望取代POP3,但是加重了服務端的載荷減輕了客戶端的壓力。
最后話題一收,開始闡述應用協(xié)議元格式:文本格式、使用單行請求和響應,有效載荷數(shù)據(jù)多行,可以隨時拓展。
需要解決的問題是簡化網(wǎng)絡間事務處理的序列化操作,因為網(wǎng)絡的帶寬昂貴的多。比如HTTP,非常簡單和通用,需要在安全和便利間做出抉擇,基于HTTP協(xié)議的其他協(xié)議很方便,但是缺點也很明顯:完全客戶端驅動,要額外接收HTTP報警信息。
還有其他的例子,比如BEEP使用二進制包序列,且支持服務端推送信息,XML-RPC、SOAP、Jabber,XML...雖然它們已經(jīng)隨著歷史逐漸沒落了,但它們的優(yōu)點應該會被未來的HTTP2吸收,到時候給我們一個更強大的HTTP。
第六章:透明性(來點光)
優(yōu)雅軟件的第三點特點是什么呢?可顯和透明性。
透明性指可以預測到程序行為的全部或者大部分情況,可顯性指幫助人們對軟件建立正確的“做什么、怎樣做”的觀念。
可以從用戶(UI)和程序員(代碼)的角度來看待可顯性和透明性。
舉幾個例子:
| 實例名 | 特點 |
|---|---|
| audacity | 音頻軟件,UI透明 |
| fetchmail | 郵件軟件,-v選項使得程序可顯,獲得“防彈程序”榮譽 |
| GCC | 編譯器,一系列處理階段都是為了可顯而設計的,例如dif |
| kmail | 郵件程序,UI可顯透明,可以訪問具體細節(jié)但是又不顯眼 |
| SNG | 圖形文本轉換軟件,可顯但不是很透明 |
| Terminfo | 數(shù)據(jù)庫,可顯透明,使用文件系統(tǒng)作為數(shù)據(jù)庫 |
| Freeciv | 游戲,數(shù)據(jù)文件以文本格式編寫 |
那怎樣設計軟件才能實現(xiàn)透明性和可顯性呢?這就需要我們專注代碼同其他人交流的方式了。
- 不要疊放太多抽象層,增加透明性。讓其他人能夠預測程序行為。
- 編碼要求:調用深度要淺、代碼要有明顯的不變性、API正交、全局設置唯一的記錄器、實體一對一映射、容易找到給定部分、避免增加特殊情況、避免意義含糊的常量。
- 避免過度保護,對于錯誤調試應該是透明的。
- 使用可編輯的表現(xiàn)形式:編寫文本化器或者瀏覽器。
- 便于故障診斷和故障恢復,實現(xiàn)健壯性。
除此之外,還要為可維護性而設計,維護性就是其他開發(fā)者能夠順利地理解和修改軟件:
- 寧愿重構,都要拋棄辣雞代碼。
- 努力讓代碼成為活代碼而不是睡代碼和死代碼,吸引別人來開發(fā)。
- 包含開發(fā)者手冊比用戶手冊更有效。
第七章:多道程序設計(分離進程為獨立的功能)
前面三章告訴我們軟件需要有模塊性、文本化、透明性、可顯性、可維護性。這一章講述的部分更加接近實現(xiàn):如何將大型程序分解成多個協(xié)作進程,又稱多道程序設計。
首先,我們需要遵循做單件事并做好的原則,提倡分解程序。
有三種方法:降低進程生成的開銷、簡化進程間通信、使用簡單透明的文本數(shù)據(jù)格式。
但協(xié)議真正的設計挑戰(zhàn)是協(xié)議邏輯,必須看得出很有表現(xiàn)力并可防范死鎖。
另外,不要過早關注性能,我們需要從性能調整中分離復雜度控制,它們是兩碼事。于是作者強烈建議我們盡量不要使用線程,還有模塊化可以達到更好的安全性。
接著作者介紹了Unix中的幾種IPC(進程間通信)方法,從最簡的逐步介紹到難的。
- 把任務轉給專門程序
最簡單的程序協(xié)作方法。專門程序運行時不需要和父進程進行交流。
例如mutt郵件用戶代理,將所有的編輯動作統(tǒng)一到單獨的emacs進程中。
- 管道、重定向和過濾
各種IPC方法的誕生的源泉,管道約定每個程序一開始至少有兩個I/O數(shù)據(jù)流可用。
管道操作把一個程序的標準輸入連接到另一個程序的標準輸入,一系列管道被稱為管線,管道和重定向的組合威力很大,不需要單獨再寫一個完成某個功能的程序(類似鏈式調用),但它的缺點是單向性,不能讓數(shù)據(jù)雙向流動。
實例是分頁程序的管道/制作單詞表、pic2graph、bc(1)和dc(1)、以及fetchmail為什么不用管線。
- 包裝器
它隱藏了shell管線的細節(jié),被調用程序專用化。
實例是備份腳本,只需要傳參調用就好。
- 安全性包裝器和Bernstein鏈
它是高級版包裝器,需要先認證再選擇性地調用。
實例是Bernstein鏈,每個繼發(fā)程序都取代了前一階段的程序,并且可以在程序鏈中插入另一個程序來修改系統(tǒng)的行為
- 從進程
父子進程通信,只在兩種情況下使用:兩者使用的協(xié)議不重要或者那個協(xié)議是根據(jù)第五章原則設計的。(前端的mv*的父子組件通信也是這樣的)
實例是scp和ssh,scp從ssh中獲取信息制作進度條。
- 對等進程間通信
對等通道,雙向數(shù)據(jù)流。(讓我想起了angular1.x的雙向數(shù)據(jù)綁定和其他前端框架的狀態(tài)管理)
| 種類 | 特點 |
|---|---|
| 臨時文件 | 簡單,易沖突,有安全性風險 |
| 信號 | 一種軟中斷形式,信號間可能會競爭 |
| 系統(tǒng)守護程序和常規(guī)信號 | 殺進程信號和常規(guī)信號 |
| 套接字 | 從封裝網(wǎng)絡數(shù)據(jù)訪問的行為引申出來,需要指定協(xié)議族(前后端分開) |
| 共享內存 | 最快,但是必須提供硬件,自己處理競爭和死鎖 |
在使用這些IPC方法的時候,我們也需要注意一些問題:
- 不要讓IPC和API耦合在一起
- 盡量別使用二進制信息交換協(xié)議
- 避免混亂的流
- 避免遠程過程調用,它雖然會提升性能但延遲高
- 別用線程(線程恐嚇),因為作者認為它標準薄弱規(guī)范模糊
最后,我們在設計層次上的進程劃分無非就是完成程序在生命期內交換數(shù)據(jù)的任務。
設計的過程中,我們需要想清楚:
- 程序前后端分離,何時建立通信?
- 何時何地完成信息的列集和散集?
- 何時產(chǎn)生緩沖問題?
- 怎樣保證獲取信息的原子性?
第八章:微型語言(尋找歌唱的樂符)
語言本身越精簡,出bug的可能性越低,當需要一個特定規(guī)則來完成任務時,微型語言就誕生了。
它相對通用語言,體積小復雜度低。那什么情況下需要微型語言呢?當預先認識到設計一門微型語言,注意到規(guī)格說明文件像微型語言,或者老是打補丁的時候。
然后讓我們理解一下語言分類法,由簡單到復雜,是文件->微型語言->通用語言的。圖8.1很好地對當時主要語言進行了分類,它們的范疇從聲明型到命令型都有。
微型語言舉例
| 案例 | 特點 |
|---|---|
| sng | 圖文轉換,便于用通用工具編輯圖片 |
| regexp | 描述文本模型,簡單地表達了識別行為 |
| glade | 界面創(chuàng)建,專門生成XML代碼 |
| m4 | 宏處理,便于把文本拓展成其他字符串,但要慎用 |
| XSLT | 描述文本流變換,具有有限的類型分類和對外接口 |
| DWB | 公式排版,聲明性語義 |
| fetchmail的運行控制語法 | 既有聲明性特點也有命令式特點,有語法糖 |
| awk | shell語言,既不緊湊也不通用所以被廢棄 |
| PostScript | 專門用于設備排版,堆棧式語言 |
| bc和dc | 命令性語言,任意精度。dc是逆波蘭標記,bc是代數(shù)標記 |
| Emacs Lisp | 前端語言,可以描述編輯動作 |
| JS | 客戶端語言,逐漸變得通用 |
設計微型語言
當需要把問題說明規(guī)格提升一個層次時,且應用領域的域原語簡單而固定不變時,我們就需要設計一門微型語言了。
- 選擇正確的復雜度
盡量簡單,自頂向下設計,符合美學。
了解嵌入型微型語言容易被濫用,宏的安全性差。
把語言一樣的特性加進去作為事后補救措施是飲鴆止渴,一個偶爾圖靈完備的語言是其他開發(fā)者的噩夢。
- 擴展和嵌入語言
通過別的語言擴展或者嵌入進別的語言。這對實現(xiàn)命令式語言來說很好,但這個選擇決定于設計本身。
嵌入的話還需要考慮錯誤語法檢查。
- 編寫自定義語法
參考XML作為語法基礎。
功能簡單的微型語言就不要殺雞用牛刀了,遵循最小立異原則。
確實需要自定義語法的話使用yacc和lex幫助。
- 慎用宏
宏帶來的問題大于好處,因為預處理器遇到表達式而不是期望的值的時候會發(fā)生意想不到的結果。并且宏難以閱讀和調試,擾亂了錯誤診斷。
C預處理器有相應的解決辦法,但m4沒有。
- 考慮清楚設計的是微型語言還是協(xié)議
考慮語言引擎是否可被其他程序交互調用,關鍵在于事務邊界的標定程度。
第九章:生成(提升規(guī)格說明的層次)
這章的核心觀點就只有一個:把程序的邏輯轉移到數(shù)據(jù)中去。也就是數(shù)據(jù)驅動編程,讓開發(fā)者只關心數(shù)據(jù)結構而不是代碼。
數(shù)據(jù)驅動編程和OO的區(qū)別是數(shù)據(jù)實際上定義了程序的控制流,OO是封裝和固定的。數(shù)據(jù)驅動和狀態(tài)機也是有區(qū)別的,一個是自動生成代碼,一個是手工寫。
概括起來就是始終把問題層次往上推。
| 案例 | 特點 |
|---|---|
| ascii | 不維護代碼,只維護數(shù)據(jù) |
| 垃圾郵件統(tǒng)計 | 統(tǒng)計數(shù)據(jù)比精巧的模式匹配奏效 |
| fetchmailconf | 通過配置文件生成代碼 |
所以我們需要代碼生成代碼。建設性的懶惰是大師級程序員的美德。
| 案例 | 特點 |
|---|---|
| 生成ascii | 發(fā)布的源碼包含一個文件,里面是數(shù)據(jù),通過它們生成代碼 |
| 生成HTML | 數(shù)據(jù)+模板 |
第十章:配置(邁出正確的第一步)
配置是開發(fā)前啟動環(huán)境的重要步驟之一,另一個是交互通道。
什么可以配置呢?理論上一切可配置,但配置項太多會爆炸。(對新手不友好)
不如搞清楚什么不可配置:比如可以自動檢測的東西。用戶也不應該看到優(yōu)化開關,0.7秒以下藏起來。用其他程序能完成的任務就不要配置。
一般來說可以在五個地方配置:運行控制文件->系統(tǒng)環(huán)境變量->用戶運行控制文件->用戶自定義環(huán)境變量->命令行選項。
這五個地方后面會覆蓋前面的,而且越到后面變化的幾率越大,好的Unix實踐要求使用同參數(shù)選項預期壽命最匹配的機制。
- 運行控制文件
以rc結尾(比如eslintrc),存放與程序相關的聲明或命令,在程序啟動時解析。
它分系統(tǒng)的和用戶的。語法有一套通用風格,比如支持注釋、不區(qū)別空白符等??梢詼p少用戶閱讀和編輯時要接觸的新鮮事物,實例是.netrc文件:對用戶透明且遵循最小立異原則,但很難移植映射到其他操作系統(tǒng)里去。
- 環(huán)境變量
用來配置程序訪問的環(huán)境。如搜索路徑、系統(tǒng)默認值、uid、pid等關鍵信息。
也分系統(tǒng)的和用戶的。使用環(huán)境變量的時機是:變量值會根據(jù)上下文而變化或隨點文件不同而改變、不能以改變命令行調用來表述,操作系統(tǒng)間的移植同樣非常困難。
- 命令行選項
可以由腳本控制程序(如node腳本),一般以-開頭,有Unix(推薦)、GNU、X toolkit風格。
從-a到-z都被賦予了特殊的含義,某些大寫字母也是。盡一切辦法遵循最小立異原則和復用它們。
有命令行的地方就好移植。
那應該如何挑選方法呢?根據(jù)從運行控制文件->環(huán)境變量->命令行選項,是最不易改變到最易改變的原則挑選。
因為后者會覆蓋前者,并且依賴于程序在調用間隙需要保持多久的配置狀態(tài)。
實例是fetchmail,設置rc文件和環(huán)境變量,最后用命令行腳本化。
但描述的約定不是絕對的,當明白自己想要什么并且想好了出錯后怎么補救,可以讓收益大于代價,是可以放手一搏的。
第十一章:接口(Unix環(huán)境下的用戶接口設計模式)
接口是程序和程序,程序和人類通訊的媒介。在設計時要遵循與其他程序通訊的前瞻性和最小立異原則。
比如I/O有三種方式:程序、IPC、已知文件或設備。所有存在的接口風格,存在即合理。
那如何應用最小立異原則來減少用戶學習負擔呢?
讓用戶對接口產(chǎn)生熟悉感,能共生和委派就弄,不能就效仿。
從Unix接口設計的歷史:打字機->命令行->可視化,我們可以知道Unix接口鼓勵機制而非策略。
接口設計評估有五種度量標準:簡潔、表現(xiàn)力、易用、透明和腳本化能力。
- 簡潔是指操作起來容易程度
- 表現(xiàn)力指接口可以表現(xiàn)出沒有預見到的行為組合
- 易用指學習成本低
- 透明說明用戶容易理解問題域
- 腳本化是指接口能被其他程序使用
在接口發(fā)展歷史上,CLI和GNU接口之間的爭論一直存在,它們分別面向專家級用戶和初學者用戶,但我們需要權衡看待。
CLI表現(xiàn)力強、簡潔、透明、腳本化但不易用,GNU表現(xiàn)力差、易用,其他不確定(看開發(fā)者)。
比如計算器程序是一個很好的GNU程序,因為它考慮到了用戶的未來行為,這是值得的。
不過Unix程序員的編程喜好還是透明、表現(xiàn)力和可配置,所以不用說,CLI是他們的最愛。
Unix接口模式
| 模式 | 特點 |
|---|---|
| 過濾器 | 標準輸入輸出,寬進嚴出,不丟不增,例如sort |
| Cantrip | 一次性,例如clear |
| 源 | 只出,例如ls |
| 接收器 | 只進,例如打印程序 |
| 編譯器 | 轉換信息,比如gcc |
| ed | 有交互能力,例如gdb |
| Roguelike | 比GNU效率高,但難以腳本化,例如Roguelike游戲 |
| 引擎和接口分離 | 機制策略分離,遵循MVC模式,有配置者/行動者、假脫機/守護進程、驅動/引擎、C/S類型 |
| CLI服務器 | 統(tǒng)一掌控程序啟動服務器進程,例如CLI服務器 |
| 基于語言的接口 | 使用微型語言來做專門的事,例如shell |
那我們非Unix程序員怎么應用Unix接口模式呢?
答案是促進腳本化和管道線能力,接口要盡量簡單。
首先交互要分三種情況:和初級用戶、專家用戶、其他程序使用。
而且通常有幾種接口模式混合,首先封裝API邏輯,然后產(chǎn)生一個多價程序。
最重要的是cantrip、GUI、腳本接口模式,可選Roguelike模式。
然后作者闡述了網(wǎng)頁瀏覽器作為通用前端的好處,它統(tǒng)一了前端,讓前后端徹底分離。
優(yōu)點有很多,比如CGI公用網(wǎng)關接口和ajax助力前后端通信。缺點是網(wǎng)頁強迫以批處理風格處理交互操作、使用無狀態(tài)協(xié)議管理持久會話。還有語言的兼容性成為一個問題(現(xiàn)在已經(jīng)不是問題,js統(tǒng)一了)。難以腳本化或將事務自動化到后端:三層架構,前端->CGI->命令腳本。
最后作者告誡我們接口沒什么可說的就閉嘴:遵循緘默原則。
因為無用信息會干擾合作、用戶、帶寬消耗,還有長時間的操作要提供進度條而不是廢話。確認提示最好是“不”而不是“是”。
調試模式和開發(fā)模式的消息可以區(qū)別對待。
第十二章:優(yōu)化
最有效的優(yōu)化是優(yōu)化之外的事情,例如清晰干凈的設計。
作者對于優(yōu)化這件事提出了自己的特有觀點:最好啥也別做,時間會給我們答案。不寫代碼就沒有bug,這點真是無力反駁。
摩爾定律告訴我們付出得不到回報,軟件優(yōu)化的那一點點很快就被硬件升級所抵消。要做也是做降維復雜度優(yōu)化而不是常數(shù)級的,比如從O(n^2)降到O(nlogn)
真要優(yōu)化的話,先估量再優(yōu)化,找到瓶頸再談優(yōu)化。
一般來說造成瓶頸有三個原因:
- 工具誤差,根本性的問題。檢測工具的代碼執(zhí)行有誤差,可以統(tǒng)計它們的調用次數(shù)。
- 外部延遲,也是根本的,不能隨機檢測,要多次檢測。
- 過度調用,把子程序的時間開銷算到了調用程序中。
所以衡量性能時不要只收集孤立的性能數(shù)字,更應該綜合多個參數(shù),如問題規(guī)模、CPU速度、磁盤速度等,最后建模得出結論。
還需要考慮非定域性(不確定性)之害,保持代碼短小簡單,核心數(shù)據(jù)結構必須留在最快的緩存里可以盡量避免。
某些優(yōu)化是不值得的,比如循環(huán)展開。
緩存越大,緩存的開銷越大,這點也需要注意。
本章后面一部分是針對協(xié)議優(yōu)化的內容,關注點主要在吞吐量和延遲上。
總的來說要設計出良好的網(wǎng)絡協(xié)議,需要盡量避免協(xié)議的往返。
實際上盡可能使用低的時延設計和忽略帶寬成本。作者提供了三種策略減少時延:對事務批處理、允許事務重疊、緩存。
1.批處理
先把更新累積起來,最后一次性處理,比如DOM操作和DOMFragment。
2.重疊
將好幾條更新一起發(fā)送出去,阻塞和等待中間結果都是致命的,比如IMAP協(xié)議對請求做了標記。
3.緩存
兼得魚和熊掌的策略,但必須考慮更新緩存的問題,更新模式越復雜,bug越容易產(chǎn)生。
而且作者認為緩存對于SPOT原則來說是不好的,因為它純粹為了性能優(yōu)化。他建議轉用加速文件系統(tǒng)或者虛擬內存實現(xiàn)會比緩存好。所以我們在使用緩存時也應該問問自己為什么要用緩存。
第十三章:復雜度(盡可能簡單,但別簡單過了頭)
真實世界的編程就是管理復雜度的問題,我們應盡量降低復雜度。
首先,我們需要理解復雜度是什么。作者分別從橫向縱向的角度進行了比較。
橫向的復雜度有三個來源:程序員、用戶、代碼。
- 程序員-接口復雜度,可能會陷進硬撐陷阱(極端晦澀的技法)。
- 用戶-實現(xiàn)復雜度,可能會造成人力尺度陷阱(將許多底層任務拋給用戶)。
- 代碼-代碼量,可能會陷入過專用陷阱(重復代碼)。
現(xiàn)實中可以對接口復雜度和實現(xiàn)復雜度折中,做出一方面是簡潔的接口,一方面是便于傳播的簡單軟件。
RG的文章認為MIT哲學(簡潔的接口)雖然讓軟件抽象地更好,但是New Jersey模型(簡單軟件)更具傳播特性,這兩種方法的平衡就在于可以拿此換彼,例如404的出現(xiàn)。
縱向的復雜度有本質的、選擇的和偶然的復雜度。
- 本質的-有些問題天生就是復雜的,比如設計火箭程序。
- 選擇的-由工程目標決定。
- 偶然的-沒有找到實現(xiàn)規(guī)定功能集合的最簡方法。
我們必須要注意選擇和偶然復雜度的區(qū)別,偶然的可以由良好的設計去除,選擇的只能改變工程目標了。
映射復雜度

- 本質接口復雜度通常無法去除,但可以調整代碼庫規(guī)模來減少代碼復雜度。
- 選擇復雜度邊界模糊,工程所涉及的任何方面都可能產(chǎn)生實現(xiàn)復雜度。
- 偶然復雜度可以通過良好的設計避免。
- 代碼復雜度可以采用更好的工具解決,實現(xiàn)復雜度可以選擇更好的算法解決,接口復雜度著眼于更好的交互設計。
- 處理復雜度依賴于見識而非方法。
有的時候由于本質復雜度的存在,簡潔不能勝任,我們只能保證功能,犧牲簡潔了。
為了讓我們對復雜度的理解更加深刻,作者講了五個編輯器的故事。(字處理器不在討論范圍內,因為過于專用)
| 種類 | 特點 |
|---|---|
| 純文本 | 編輯器只知道其字節(jié)或者行結構 |
| 富文本 | 文本帶有屬性,如字體大小顏色等 |
| 句法感知 | 高亮,自動縮進 |
| 批命令輸出解析 | 可以編譯并捕捉錯誤 |
| 同輔助子進程交互 | 可以調試/版本控制/和其他程序通信 |
| 編輯器 | 特點 |
|---|---|
| ed | 純文本編輯 |
| vi | 四不像 |
| Sam | ed的進化版,新增了功能 |
| Emacs | 大而全 |
| Wily | 鼠標控制 |
由此作者總結了一下,編輯器的適當規(guī)模應該是什么樣的。
首先甄別它們的復雜度:ed最簡單,Emacs最復雜,vi是折中派,Sam繼承了ed的簡潔,Wily優(yōu)雅但有過于依賴鼠標的代價。接著批判了vi:折中無用。(雖然現(xiàn)在vi還是很火)
不過最后一句話說的還是好:少吃多干還是多吃多干取決于時代。(vscode應該是多吃多干,sublime應該是少吃多干)
結尾的時候引申到如何構建軟件的適度規(guī)模:選擇需要管理的上下文環(huán)境,并且按照邊界所允許的最小化方式構建程序。先證明其他方法行不通時再編寫大型程序。
具體實現(xiàn)
第十四章到第十六章介紹了Unix中涉及到的語言、工具以及輪子,我們前端也需要考慮自身領域的相關問題,這對我們真正編碼的時候提升效率是非常有幫助的。
第十四章:語言(C還是非C)
Unix下的語言是豐饒的,并且鼓勵專門領域語言的設計。一方面,C語言是Unix的伴生語言,另一方面,各種腳本語言在動態(tài)存儲管理的自動化上有巨大優(yōu)勢。
C和Unix的關系是巧妙的,沒有Unix就沒有C,沒有C就沒有如今Unix文化的繁榮,C和C++取代了匯編語言在工業(yè)界的地位,重新掀起了一波技術浪潮。
雖然C和C++對要求極高的程序有意義,但損耗了程序員的精力。
隨之而來急劇下降的成本又改變了編程的經(jīng)濟含義。軟件的復雜化說明自動化內存管理越來越重要,而且真正性能的損失往往來自外界。(網(wǎng)絡延遲、事件等待等)
到當今這個年代,混合策略才有可能使得效率最大化。即一種在主語言中嵌入其他語言的策略。
比如可以嵌入內存管理器完成內存管理,嵌入腳本膠合邏輯。高級shell編程甚至可以自由混合語言編程。
接著作者對當時的主流語言進行了一番評估,因為熟悉語言才能更好地使用和組合它們。
| 語言 | 特點 |
|---|---|
| C | 資源效率最接近機器語言,但資源管理非常困難 |
| C++ | 效率高,支持OO和泛型編程,但非常難用,鼓勵過于復雜的設計 |
| Shell | 完成小型任務自然快捷,但大型腳本必須依賴大量輔助命令造成兼容性問題 |
| Perl | 強大的工具語言以及正則匹配,但大型項目不優(yōu)雅、難以維護 |
| Tcl | 節(jié)儉緊湊的設計和作為解釋器語言的可拓展性,但數(shù)據(jù)結構和命名空間等很怪異以至于難以用于大型項目 |
| Python | 為嵌入而生的膠水語言,代碼清晰優(yōu)雅,但效率不高 |
| Java | 自動管理內存并且支持OO,但設計的有些復雜而且沒達到一次編寫處處運行的目的 |
| Emacs Lisp | 結合了Lisp,優(yōu)雅、自動管理內存,但難以移植、性能差 |
作者還對這幾種語言的未來趨勢做了預測:C/C++/Java不變、Tcl/Perl衰退、Python增長,事實證明他基本上是對的。
前面分析了那么多,該到自己動手選擇編程工具包的時候了,因為GUI工具包是會影響編程狀態(tài)的,而且某些語言和工具包的綁定有特定要求,比如Qt屹立不倒,但我用vsc。
第十五章:工具(開發(fā)的技術)
語言選好了,工欲善其事必先利其器,接著就是選工具了。
首先,我們需要一個對開發(fā)者友好的操作系統(tǒng),像Unix就沒有固定的IDE,需要自己組合工具完成IDE的功能。這樣可以讓程序員更加專注于設計,以編輯/編譯/調試為中心,其他細節(jié)用工具完成。前端也需要自己組合。
- 編輯器
作者主要對比了vi和Emacs,但我認為vi類似于sublime,可以靈活拓展,Emacs類似于vsc,大而全,不過兩者兼用,用于不同的場景才是最佳策略。
- 專用代碼生成器
作者以lex和yacc這兩款生成語言詞法分析器的工具為例,介紹了lex是從輸入流中獲取標記符號,而yacc是解析一系列標記符號來檢查是否符合語法。
但lex意外地被用于各種模式識別,輸入一堆,最后找出某個模式。
工具生成的代碼還是比手工正確高效。
- 自動化編譯
把源碼進行裝配打包發(fā)布才是最重要的,以make為例
它會尋找代碼間的依賴關系從而生成正確的打包版本(類似webpack),但要注意不能非常復雜,比如遞歸make。
一些腳本語言生成任務所需的文件也是很方便的,有all、test、clean、install等命令,類似npm。
makefile的可移植性和分析依賴能力靠幾個工具完成:makedepend、Imake、autoconf、automake等。
- 版本控制系統(tǒng)
為了追蹤變化,特別是bug,查看作者、時間、內容等,我們需要版本控制系統(tǒng),而計算機更加擅長這些細節(jié)。
手工版本控制隱性成本非常高,自動化的版本控制能夠保存項目的歷史評注并避免修改沖突。
舉例為VCS,SVN是CVS的衍生版本(基于文件),GIT是現(xiàn)代版本控制系統(tǒng)(基于變化)。
- 運行期調試工具
能夠打斷點,檢查程序狀態(tài),可控執(zhí)行某個單一語句層次的部分,這是透明性設計的另一幫手。
- 性能分析工具
程序90%的執(zhí)行時間都耗費在10%的代碼上,性能分析軟件幫助定位問題,這樣就可以優(yōu)化關鍵的10%的代碼并遵循之前的優(yōu)化原則。
- 整合工具
編輯/編譯/測試/調試/版本控制...一體的工具,對前端來說vsc+chrome可以完成95%的工作,不是IDE勝似IDE。
第十六章:重用(論不要重新發(fā)明輪子)
無為代碼,天下希及。這個無為是指最經(jīng)濟的行為,對無論是人員資本還是經(jīng)濟收益都有好處。
而讓代碼無為就是重用代碼,重用代碼又是避免發(fā)明輪子的最有效方法。Unix里里外外都支持重用,組合優(yōu)先于獨立。
作者還特意講了一個豬小兵的故事,這是千千萬萬程序員的縮影:
我們在工作中重用的代碼可能會有問題,不得逼我們重造一個輪子。但重用代碼是技術問題、知識產(chǎn)權壁壘、行政問題以及個人自我意識的綜合,所以代碼專用化還是開放化讓程序員們糾結。
不過,決定重用了,就必須透明。比如用源碼和注釋幫助使用者理解代碼,牢記只有變化才是永恒的,源碼可以延續(xù)而二進制碼不行。
那重用和開源的關系又是什么樣呢?作者說開源和重用就像愛情和繁殖的關系一樣,開源也是為了重用自然而然發(fā)展而成保護透明性優(yōu)勢的策略。對開發(fā)者來說,保證了經(jīng)驗的價值,這也是職業(yè)發(fā)展的動力。
開放源碼是從意識形態(tài)上解決這些所有問題的優(yōu)先方法。
另外一點是,開源質量通常大于閉源。因為同行復議保證了標準,評估開源代碼的方法是閱讀其文檔和它的部分代碼,如果有一定年頭、反饋、協(xié)同作者數(shù)、社區(qū),這份代碼就是質量高的。
去哪找呢?代碼庫和專用開源網(wǎng)站。作者推薦了SourceForge、Freshmeat等網(wǎng)站,現(xiàn)在應該是Github。
找到重用代碼就是節(jié)約自己的編碼時間,閱讀代碼的元數(shù)據(jù)并且試一試對自己是有好處的,并且閱讀代碼的細節(jié)也是為未來投資。
但使用開源軟件還需要注意幾個問題:考慮質量、文檔、許可證。
文檔的話,專用文檔不如How To&FAQ等搜索來理解的快。
許可證相關的我們需要知道版權和許可證是兩碼事,誰是版權所有者不重要,關鍵是許可證條款。它讓我們使用、修改代碼的權利有限制,標準許可證有MIT、BSD等,GPL帶有病毒性質,LGPL和MPL則削弱了這一點。另外記得,找律師只有1%的幫助...
社區(qū)的力量
最后幾章揭示了Unix為何生命力如此長久的原因,在人和技術的平衡關系上做了非常仔細而微妙的分析。
第十七章:軟件可移植性與遵循標準
軟件開源了,你想讓更多的人使用你的軟件,但是傳播的障礙常常來自操作系統(tǒng)和硬件結構。
移植性一直是Unix的主要優(yōu)勢,所以一旦設想軟件項目生命周期很短,就容易犯錯。
只要在架構、接口和實現(xiàn)上,API是穩(wěn)定的,其他特殊細節(jié)都是無關緊要的。
比如,C和Unix緊密關聯(lián),是硬件和操作系統(tǒng)間的薄膠合層。它是在1971年誕生的,后期逐步引入typedef、union等操作符,版本7引入了枚舉,并且將結構體和union作為一等公民。C語言標準造成了“K&R C”和“ANSIC”的區(qū)別,并且產(chǎn)生了一個很好的實踐:在標準化之前,先實現(xiàn)各種pollify。
再延伸到Unix標準,同樣使用公開標準作為API說明。雖然經(jīng)過了分裂和內戰(zhàn),但Unix的標準在實踐中得以奠定下來。
開源社區(qū)為了標準化,也需要確保源碼的兼容性很強。
舉個例子:IETF和RFC標準化過程,里面就體現(xiàn)了互聯(lián)網(wǎng)工程任務組的思維方式:標準必須來自于一個可用原型實現(xiàn)的經(jīng)驗。
當然也有理想化的標準,比如臭名昭著的七層OSI模型。我們要考慮這點:在成為標準之前,實現(xiàn)的要求是越來越高的。所以只有當草案標準經(jīng)過了實現(xiàn)的廣泛測試并且達到了普遍接受的程度,就真正成為標準了。
對此作者打了個形象的比方:規(guī)格是DNA,代碼是RNA。因為代碼是可棄的,標準才是應該保留完善的。
代碼從屬于標準,先做一個原型再不斷地測試和演進才是好辦法,生成半自動化的測試套件也是一個主要優(yōu)勢,可以穩(wěn)步迭代。至于相關的系統(tǒng)行為爭論可以在規(guī)則功能層面解決,非規(guī)格(功能)即bug。
話題順著到可移植性編程上,這個問題看似是準空間問題,實際上時間上的持久性同樣重要。
首要問題是選擇語言,作者對當時流行的語言做了移植性分析:
| 語言 | 移植性特點 |
|---|---|
| C | 高,但對于IPC、線程和GUI接口有困難 |
| C++ | 類似C |
| Shell | 差,大部分shell使用了其他可移植性差的工具 |
| Perl | 良,看情況 |
| Python | 優(yōu)秀 |
| Tcl | 一般,隨項目復雜度有差異(看依賴) |
| Java | 出色,但幾個版本間的GUI有兼容問題 |
| Emacs Lisp | 相當好,問題出在使用C接口的地方 |
總的來說就是避免系統(tǒng)依賴性,發(fā)布源碼勝過二進制碼,不要想著幫助不大的移植工具。
所以現(xiàn)在js成了可移植性語言之王。JavaScript is everywhere。
另外一點和移植化有關的是國際化,實現(xiàn)它我們需要分離信息庫和代碼,并且盡量使用UTF8字符集,使用正則時注意字符范圍就好了。
那可移植性/開放標準和開放源碼有什么關系呢?可移植性需要標準,而開源促進了標準化。另外,不要依賴專有技術,哪天作者跳坑就GG。
第十八章:文檔(向網(wǎng)絡世界闡釋代碼)
Unix最初的目的就是整理文檔,troff格式器是始祖,現(xiàn)在的趨勢是朝著html和url鏈接發(fā)展。
首先讓我們區(qū)分一下標記型和可視型的文檔:一種是面向程序員的,一種是面向初級用戶的。
標記型又分表現(xiàn)型和結構型的,而大多數(shù)以標記為中心的文檔系統(tǒng)都支持宏。
Unix風格的文檔具備幾個文化和技術特征:
- 偏愛大文檔
- 寫給技術人員看,手冊頁往往包含一個BUGS部分
- 擅長編寫參考書籍
各種Unix文檔格式
| 文檔類型 | 特征 |
|---|---|
| troff和DWT | 表示層語言不如結構層語言,大量用于技術文檔 |
| TEX | 使用輔助程序比如LETEX編寫,大量用于數(shù)學和科學領域 |
| Texinfo | 可以生成HTML |
| POD | Perl的標記系統(tǒng),可以生成手冊但不能生成HTML |
| HTML | 未來趨勢,在生成索引上有問題 |
| DocBook | XML文檔類型定義,可以轉換成HTML、PDF等格式 |
于是書中大膽的預言未來的出路是XML一統(tǒng)天下...然而現(xiàn)在json橫空出世...
對于DocBook,作者還特意描述了一下:有一條轉換工具鏈,先驗證是否是符合正確的文檔格式,再根據(jù)樣式單加樣式最后輸出。
但是最后還是批判了這條又臭又長的工具鏈,即使優(yōu)化成FOP了還是不咋地。
最后本章總結了編寫文檔的最佳實踐:就是不要忽悠讀者。
- 數(shù)量多不會被認為質量高
- 信息密度要適中
- 大項目最好發(fā)布手冊頁/教程/常見問題解答列表
- 文檔中要有readme
- 考慮新手用戶,技術名詞盡量用全稱
- 文檔格式應該易于傳播
第十九章:開源(在社區(qū)中編程)
Unix在開放源碼上就做了很好的表率,將找/改bug的任務分解成多個并行的子任務,然后眾力編程。
而開源有如下幾個特點:
- 源碼公開
- 盡早發(fā)布/經(jīng)常發(fā)布,前提是項目正常運行
- 給貢獻給予表揚
- 開源項目管理盡量自動化
之后便是本章的重點:如何與開源開發(fā)者協(xié)同工作的最佳實踐
- 良好的修補實踐
1.1 是否換位思考/知道合并的后果
1.2 發(fā)送dif部分/針對當前版本/不要包含可生成文件/不要發(fā)送系統(tǒng)自動拓展的字段
1.3 在補丁中包含文檔/解釋/有用的注釋
通過代碼質量評估補丁
良好的命名實踐
2.1 使用GNU風格,例如foobar-1.2.3.tar。gz
2.2 文件名/版本和區(qū)分度是最重要的
2.3 尊重適當?shù)谋镜丶s定
2.4 選擇容易鍵入的前綴良好的開發(fā)實踐
3.1 不要依賴專有代碼
3.2 使用GNU自動工具管理項目
3.3 先測試再發(fā)布代碼
3.4 發(fā)布前對代碼進行健全檢查(能夠捕捉到錯誤)
3.5 對readme進行拼寫檢查
3.6 考慮移植性良好的發(fā)布制作實踐
4.1 確保打包文件總是解包到單一新目錄下
4.2 包含README文件(項目介紹、項目demo演示、環(huán)境問題、關鍵架構、編譯安裝指令、維護者光榮榜、項目新聞、項目郵件列表地址等)
4.3 尊重和遵從標準文件命名實踐(看社區(qū)的習俗)
4.4 為可升級性設計
4.5 提供RPM(類似npm)
4.6 提供校驗和
供他人更好地下載、獲取和使用
- 良好的交流實踐
5.1 在社區(qū)和社交平臺發(fā)公告
5.2 建立一個網(wǎng)站
5.3 提供項目郵件列表
5.4 發(fā)布到主要的檔案站點
便于招攬用戶與合作者
本章最后分析了許可證如何挑選,畢竟它會對軟件施加限制。
雖然可以直接放在公共域,但使用某個標準許可證可以避免很多爭論。
有MIT、BSD、GPL等許可證,具體可以看阮一峰的如何選擇開源許可證
終章:危機與機遇
本書的結尾章,總結了過去如何應對的設計挑戰(zhàn),以及未來確定需要解決的問題和有待開拓的機會。
Unix最終要的是什么?當然是它的文化,而從傳統(tǒng)來看它有平質和偶然屬性,平質屬性和偶然屬性是可以互相轉化的。
在歷史的長河中,三個特殊的技術變化驅動了Unix設計風格中的重大變革:網(wǎng)絡互聯(lián)、位圖圖形顯示和PC普及。
在這其中Unix一直保持著獨有的設計準則:模塊化、透明性、機制同策略分離等。
有人嘗試重做Unix(Plan9),但最終失敗,不過給予了Unix發(fā)展的啟迪。這是一個比Unix更Unix的設計,并且還增加了一個概念:私有命名空間,但更優(yōu)秀解決方案的最危險敵人,就是一個現(xiàn)存的、足夠優(yōu)秀的代碼庫,沒有質變,誰會改變自己的慣性使用新事物呢?
當然,Unix設計中也存在許多問題,這里著重討論幾個存在爭論的失敗之處:
- Unix文件只有字節(jié)
- Unix對GUI的支持孱弱
- 文件刪除不可撤銷
- 假定文件系統(tǒng)是靜態(tài)的
- 作業(yè)控制設計拙劣
- API的異常處理不好
- 設備中插入鉤子的方法(ioctl和fcntl)是個雞肋
- 安全模型太過原始
- 名字種類太多
- 文件系統(tǒng)的爭論
- 朝向全局互聯(lián)網(wǎng)地址空間
跳出程序員的眼界,來看看整個社會環(huán)境下,Unix如何發(fā)展:
首先要獲得持續(xù)的經(jīng)濟支持,提高程序員的社會價值,然后組織終端用戶測試,獲取良好的反饋,最后要反對微軟/好萊塢等巨頭,為自由而斗爭。
Unix文化中也有問題:內部轉型的小問題和克服歷史上的優(yōu)越感的大問題。
比如和Mac之爭,但Mac和Unix的設計哲學都有正確的一面,應該互相理解。不要把自己從騎士變成惡龍。
舍得拋棄過去,不再過分依賴那些已經(jīng)很好地為我們工作過的設想。
勝利也不是全面的,低端市場和非技術用戶被忽略了。
最后的最后,作者語重心長的說:
“我們能贏,只要我們想贏?!?/p>