UNIX程序設計藝術
UNIX系統與其說是一操作系統,不如說是一口述史。-- Neal Stephenson
在學問和專門技術之間存在天壤之別。學問讓你推演去做正確的事情;專門技術構成正確的事情的一種習慣性思維,幾乎根本不需要有意識的去思考。
這本書裡面有大量的學問,但是它主要是關於專門技術的。它將會設法教你UNIX專家所知道的關於UNIX開發的事情,而不是意識到他們所知道的。因此,比起大部分UNIX書,它是較少關於技術的,並且更多的是關於共享的文化-顯示和隱式的文化,有意識和無意識的傳統。它不是一本「怎樣去做」的書,而是一本「為什麼這樣做」的書。
為什麼這樣做有重大的實際的重要性,因為太多的軟體被拙劣的設計。它們中的大部分遭受了膨脹,維護起來是極度地困難,並且太困難而不能移植到新的平台上或以最初的程序員沒有預料到的方式擴展。這些問題是糟糕的設計的徵召。我們希望這本書的讀者將會學到UNIX必須教授的關於好的設計方面的東西。
這本書被劃分成四個部分:背景,設計,工具集,和社區。第一部分(背景)是哲學和歷史,幫助為接下來所述各種的東西提供基礎和激勵。第二部分(設計)展開UNIX哲學的原則進入到關於設計和實現的更細節的建議。第三部分(工具集)集中在UNIX為幫助你解決問題所提供的軟體。第四部分(社區)是關於那些使UNIX文化在它所涉及的方面如此高效的人與人的交往和協定。
因為這本書是關於共享文化的,我從未計劃單獨寫它。你將會注意到這些文字包含了來自傑出的UNIX開發者,UNIX傳統的塑造者的特約發表。在我邀請這些權威者來評論和討論這些文字的期間,這本書經受了一個長期的公開的審核過程。在這本書的最終版本中,不僅覆蓋了那個審核過程的結果,這些佳賓也被鼓勵用他們自己的語態說話,引申和衍變,甚至和這些文字的主線不一致。
在這本書中,當我使用編輯上的「我們」的時候,這不是假裝無所不知,而是反映了試圖鏈接整個社區的專門技術的事實。
因為這本書的宗旨是傳播文化,它比一般的技術書籍包含了更多的關於歷史和民間傳說和一些題外話。享受它吧;這些東西,也是作為一個UNIX程序員,你的教育的一部分。這些歷史上的細節,沒有哪一個單獨上是至關重要的,但是它們全部的整體形態是重要的。我們認為用這種方式,它構造了一個更加有趣的故事。更重要的,明白UNIX從那裡來的和是怎樣走上這條路的,它會幫助你發展出對於UNIX風格的直覺的感覺。
出於同樣的原因,我們拒絕寫那些似乎結束的歷史。你會發現異乎尋常地許許多多的對於我正在寫這本書的時候的參考。我們不希望假裝當前的實踐反映了一些註定命運永恆的和完美的邏輯結果。寫作時候的參考對於讀者意味著警報,二年或三年或五年以後,這些相關的事實記錄也許已經過時了,並且應該被仔細的檢查。
這本書的其它方面既不是C語言的教程,也不是UNIX命令和API的指南。它不是為sed 或yacc 或Perl 或 Python的參考。它不是網路編程的初級讀物,也不是對於神秘的X的詳盡的指南。它也不是UNIX的內核和結構的漫遊。其它書籍更好的覆蓋了這些細節,這本書會適時給你指出它們。
超越了所有的這些技術細節,UNIX文化擁有一個發展超過數百萬人年的嫻熟的努力這種字面意義上的非書面的工程傳統。這本書是帶著理解那個傳統,和加入它的設計模式到你的工具箱中這樣的信仰寫的,它將會幫助你成為一個更好的程序員和設計師。
文化由人構成,並且學習UNIX的傳統方式是通過耳濡目染,從其它人那裡和通過民間傳說。這本書不是取代人與人之間的文化互滲,但是它通過允許你分接其他人的經驗,能幫助加速這個過程。
註:【1】在1969和2003年之間的35年是一段很長的時間。在那期間,由於歷史趨勢的彎曲,大量的UNIX站點都逝去了,或許在某處,超過5千萬的人年的工作量已經幹勁十足地投入到了遍及全球的UNIX開發之中。
n 適合的讀者:
如果你是一個經驗豐富的UNIX程序員,經常處在要麼指導初學編程的新手,要麼和其他操作系統擁護者進行爭論這樣的場合。同時,你發現你很難把UNIX所帶來的好處講清楚。那麼,這本書正在你所需要的。
如果你是一個在其它操作系統上有編程經驗的C、C++或JAVA程序員,並且準備啟動一個基於UNIX的項目。那麼,你應該閱讀此書。
如果你是一個UNIX操作系統下,具有初級到中級水平的用戶。但是,只有比較少的開發經驗。並且,想學習怎樣在UNIX下有效的設計軟體。那麼,你應該閱讀此書。
如果你是一個已經認識到UNIX傳統也許有一些東西能讓你從中受益的非UNIX程序員。那麼,你應該閱讀此書。我們確信你是對的,並且UNIX哲學同樣適用其它操作系統。因此,比起其它的一般UNIX書籍,我們在這裡更多關注非UNIX環境(特別是micorsoft操作系統)。並且,我們可以說,這裡的工具和學習例子在一定程度上都是可移植的。
如果你是一個程序架構師,要為一個主流的通用市場或是縱嚮應用程序考慮平台的多樣性或是各種不同的實現策略。那麼,你應該閱讀此書。它會讓你明白UNIX作為一個開發平台的強大,和作為一種開發方式的開源的unix傳統。
如果你正在尋找關於C語言編程細節或是怎樣使用UNIX內核編程介面方面的書籍,那麼這本書不是你需要的。關於這方面已經有很多優秀的書籍:在展示UNIX API的書籍中,《UNIX高級環境編程》是其中非常優秀的一部。《程序設計實踐》也推薦所有的C程序員閱讀(事實上是使用任何語言的程序員)。
n 怎樣使用本書
這本書是理論實踐相結合的。一部分是至理名言和通用的,另一部分是檢驗細節的UNIX下開發的例子學習。我們會在通用的原則和至理名言前面或是後面,用實例來描述它們:這些實例不是從玩具似的示範性程序中抽取出來的,而是每天都使用的真正的能工作的代碼。
我們故意避免了讓書里充斥大量的代碼和文件清單似的例子,即使這樣做可能會讓書好寫一些(有些地方可能會容易讀一些)。很多編程書籍給出了太多的低層次的細節和例子,但是沒有給讀者對於說講內容的一個更高層次上的認識。這這本書里,我們寧願在反方向走向歧途。
因此,當你經常被應邀閱讀代碼和規範文件的時候,事實上只有少量相關內容被包含在書里。相反,我們會在web給出完整的例子。
有意思的是,這些例子能幫助鞏固你所學到的原則變成近乎直覺的工作知識。理想情況下,你應該以近似於運行的UNIX系統上控制台的方式來閱讀本書,手邊在準備一個瀏覽器。任何UNIX系統都支持這樣的方式,但是在LINUX系統上,為了對系統進行檢查,這裡用於例子學習的軟體是預先安裝的和立即可用的。該書中的各種指示物用來進行瀏覽和試驗。這些指示器的介紹是按部就班的,因此,暫時的研究偏離主題不會打斷那些必須連續的闡述。
注意:儘管我們已經盡一切力量去引用穩定可用的URL,但是我們辦法來擔保這一點。如果你發現引用連接已經失效了,使用你喜歡用的WEB搜索引擎,用經驗常識和做短語搜索。這裡我們建議你的方式和我們自己引用URL的方式差不多。
這本書里的大部分縮略語在第一次使用的時候都是以完整的形式出現。為了方便起見,我們在附錄里提供了術語表。
參考資料通常按作者的名字。本應在書中正文出現的URL介紹或是我們懷疑的很容易失效的URL,還包括一些題外話、戰爭故事、玩笑,這些我們都用標號的腳註的方式來表示。
為了讓這本書更容易被技術不是很強的人接受,我們邀請了一些非程序員人士進行閱讀,並確定了那些有些模糊,但是對說明流程很重要的術語。我們還用腳註的方式給出了對於有經驗程序員不需要的一些基本術語的定義。
注【2】:這些特別腳註是獻給Terry Pratchett的,他的腳註的使用是相當。。。令人鼓舞的。
n 相關參考資料
以前,涉足這個領域的一些早期的UNIX開發者撰寫了一些著名的論文和書籍。Kernighan 和 Pike的《unix編程環境》是其中比較優秀的,也是被認為很經典的。不過,現在看來,它有點過時了。它沒有覆蓋internet、WWW和象Perl, Tcl, and Python這樣的解釋語言的新潮流。
在我們寫這本書的途中,我們學習了Mike Gancarz的《unix哲學》。這本書在闡述範圍上十分優秀,但是依然沒有嘗試覆蓋我們認為有必要闡述的所有領域。儘管這樣,我們還是要感謝作者的暗示:非常簡潔的UNIX設計模式是UNIX持久穩固和成功的非常重要的因素。
和本書相比,《程序員修鍊之道》在有細微差別的各種不同的軟體設計工藝(更多的是代碼,較少關於高級別的問題劃分)中,給出好的設計實踐權衡方面,更像是一個幽默詼諧,並且明智的專題論文。作者的觀點是UNIX下經驗的產物,同時也是本書的有力補充。
在UNIX傳統里,《程序設計實踐》從一種更深入的角度覆蓋了和《程序員修鍊之道》部分相同的立場。
最後(並且被認為有煽動傾向),我們推薦的是《Zen Flesh Zen Bones》,它們是佛教原始來源的重要的收藏品。對佛教的參考散落在整個書籍裡面。我們引用它是因為,佛教提供了對於表達軟體設計被證明是重要的思想的辭彙,而採用其它方式卻很難記住。帶有宗教信仰的讀者會認為佛教不是信仰,而只是治療學的一種智力訓練的形式,從純粹的無神論角度來看,佛教確實是這樣的。
n 使用本書的約定俗成
術語UNIX是開放組織技術上和法律上的商標,並且只有那些通過了開放組織精心製作的標準化測試而被鑒定的操作系統才能正式使用。在本書中,我們使用當前程序員群體中所認為的非嚴格的,更廣泛意義上的UNIX含義,它是指或者是從共同祖先-貝爾實驗室的原始UNIX代碼繼承下來,或者是模仿它的後代進行編寫的任何操作系統(無論是不是正式的UNIX商標)。特別的,從這個意義上來講,LINUX就是UNIX。
本書採用UNIX手冊頁的習慣,在工具名稱後面用括起來的手冊段進行標誌,當我們想強調它是一個UNIX命令的時候,通常把它放到第一部分的介紹中。因此,例如,當讀到munger(1)時候,munger作為一個程序,它的文檔描述將會出現在UNIX手冊頁第一段中,當然如果你的系統提供它。第二段是系統調用,第三段是C庫調用,段5是文件格式和協議,段8是系統管理工具。其它段隨著UNIX系統的不同而不同,但是本書沒有給出相關引用。進一步,你可以在SHELL提示符下敲入man 1 man 以得到更多相關內容(老的系統V版本UNIX,可能要求敲入man -s 1 man)。
有時候,我們提到一個UNIX程序的時候(例如,Emacs),沒有手冊段作為後綴,並且首字母大寫。這是一個暗示,它的名字實踐代表了本質上有相同功能的精心組織的UNIX程序族。並且,我們正在討論的是它們的共性,例如,Emaces,它包含了xemacs。
在本書後面的不同知識點中,我們參照「守舊派」和「新學派」的方式。就像RAP音樂一樣,新學派大約開始於1990年。在那個時代環境中,它和腳本語言的興起,GUIs,開源UNIX和WEB聯繫在一起。守舊派指的是1990年以前(特別是1985年以前),昂貴的(共享的)計算機,專用的UNIX,shell腳本編程,C語言幾乎統制了一切的那個世界。這裡的差別是值得指出的,因為廉價的和沒有內存限制的機器促成了UNIX編程風格上的重大改變。
n 我們的學習例子
為了證明一個觀點,大部分編程方面的書籍都依賴於特殊構造起來的玩具似的例子。這本書不是這樣的。我們的學習例子將是真實的,早就存在我們每天都使用的產品中的片斷。下面是一些主要的產品:
cdrtools/xcdroast
這兩個分開的工程經常在一起使用。cdrtools包是刻錄CD-ROMs的一套CLI工具。以「cdrtools」為關鍵字在WEB上搜索一下。xcdroast程序是cdrtools的GUI前端。詳見xcdroast project site.
fetchmail
程序fetchmail採用POP3 或IMAP郵件傳輸協議,從遠程郵件伺服器上獲取郵件。詳見fetchmail home page(或在WEB上用「fetchmail」進行搜索)。
GIMP
GIMP(GNU的圖像處理程序)是一款具有塗、繪、圖像處理全方位功能的圖像處理軟體,它採用獨特的方式能夠處理種類繁多的圖形格式。在GIMP home page上能夠獲得源碼。
mutt
在當前種類繁多的基於文本的UNIX電子郵件代理中,mutt郵件個人代理是其中最好的一款,它對MIME (Multipurpose Internet Mail Extensions)有非常好的支持,並且the use of privacy aids,例如PGP (Pretty Good Privacy) 和GPG (GNU Privacy Guard)。可以從Mutt project 站點上獲得源代碼和可執行程序。
xmlto
xmlto命令可以轉換DocBook和其它XML文檔為各種各樣的輸出格式,包括html,text和PostScript,可以在xmlto project site.上獲得源碼和文檔。
為了降低讀者理解這些例子所需要的代碼數量,我們儘力選擇了那些可以一例多用,可以闡述好幾個不同的設計原則和慣例的學習例子。出於同樣的原因,好多例子是從我的項目里挑選出來的。這裡沒有任何暗示說來自於我的項目的例子可能是最好的,我僅僅是十分熟悉它們,這對於例子的多用途說明是有好處的。
n 感謝
特約的貢獻者(Ken Arnold, Steven M. Bellovin, Stuart Feldman, Jim Gettys, Steve Johnson, Brian Kernighan, David Korn, Mike Lesk, Doug McIlroy, Marshall Kirk McKusick, Keith Packard, Henry Spencer, and Ken Thompson)為這本書加入了大量的價值。特別是Doug McIlroy,在他的評論的透徹性和他貢獻的深度上,遠遠超過了職責所求,顯示出了和他30年前帶給的為管理最初的UNIX科研小組的同樣優秀的關注和奉獻。
特別感謝Rob Landley和我的妻子Catherine Raymond,他們二個人對原稿的草稿作出了深入細緻的逐行的審訂。Rob深刻的和專心的評論事實上比在最終稿中的一整章內容更令人鼓舞,並且他做了大量的關於它呈現的組織和範圍的工作。如果他寫了全部促進我去改善的文字,我將不得不稱他為合著者。Cathy是我的試驗讀者,代表了非技術用戶。為了延伸這本書,讓那些還不是程序員的讀者所能理解,這些主要都是他做的。
在我花費寫這本書的超過5年的時間裡,它從許多其他人的討論那裡獲益。Mark M. Miller幫助我完成了關於線程的啟發。John Cowan提供了一些關於介面設計模式的見識,wily和VM/CMS的研究案的草稿,並且Jef Raskin給我展示了最小詫異原則是從那裡來的。UIUC系統架構組在早期的章節上貢獻了有益的反饋。UNIX做錯了什麼和深入的伸縮性這樣的節是由於他們審閱的直接靈感。Russell J. Nelson 貢獻了在第七章中鏈系的關於Bernstein的材料。Jay Maynard貢獻了第三章中在MVS學習例子中的大部分材料。Les Hatton在語言一章中提供了許多有益的評論和由此啟發的第四章中關於最佳的模塊大小中的部分。David A. Wheeler貢獻了很多觀察入微的評論和一些例子研究材料,特別是在設計部分。俄國人Cox幫助改進了9張平面圖的概觀。Dennis Ritchie在關於C的一些歷史點上糾正了我。
許許多多的UNIX程序員,比這裡列出的多得多,在2003年的1月到6月之間的這本書的公開審核期間,貢獻了建議和評論。一如既往,我發現在WEB上的開放的同行審查過程具有熱情的挑戰和熱情的回報。也一如既往,對工作結果的任何錯誤的責任歸我自己。
這本書的說明風格和涉及的一些內容己經被設計模式運動所影響。事實上,我動搖過給這本書起名叫UNIX設計模式的想法。我沒有那麼做,因為我不贊同這個運動中的一些隱式的中心法則,並且沒有感覺到使用它所有的正式的註解的需要或承擔它文化的包袱。然而,我的方法無疑的受到了Christopher的影響。
Alexander的工作(尤其是永恆的建築方式和一種模式語言),我欠「四人幫」和他們學派的其它成員的大量的人情,因為他們給我展示了怎樣可能的去使用Alexander的見識站在一個高層次上,而不僅是全然含糊的和無意義的概率,去討論軟體的設計。留意的讀者應該明白設計模式:可復用面向對象軟體的基礎,是對設計模式的入門介紹。
當然,這本書的標題參考了Donald Knuth的計算機編程藝術。儘管和UNIX傳統沒有特定的關聯,Knuth己經對我們所有人到造成了影響。
編輯具有的先見之明和想像力不是象他們看起來一樣普通。Mark Taub就是這樣的一個。他在一個停頓的項目上開鑿出價值,並且巧妙的把我推入完成它。文案編輯對於散文風格很會賞析,並且有足夠的能力改善那些不像他們自己的風格,甚至是很少有共同點作品,但是Mary Lou Nohr 就達到了那個水平。Jerry Votta 抓住了我對於封面的觀點,並且使它看起來比我想像的更好。在Addison-Wesley出版社裡的全體人員為使編輯的和生產的過程儘可能無痛苦達到了很高的水平,並且不僅僅在文字上愉快的接納了我任性的控制傾向,而是深入到了這本書的視覺設計,藝術,和市場的這些細節上。
注【3】:Alexander的工作的正確評價,帶有重要的部分的在線版本的鏈接,可以在《Some Notes on Christopher Alexander》上發現。
2、第一部分:背景2.1、哲學n 文化?什麼的文化?
這是一本關於UNIX編程方面的書籍,但是在這本書里,我們將會大量使用文化、藝術、哲學這樣的用詞。如果你不是程序員,或者是和UNIX接觸比較少的程序員,這些對於你來說,可能有點奇怪。但是,UNIX有一種文化;它有獨具特色的編程藝術;並且伴隨著它有一種很強大的設計哲學。理解這些傳統會幫助你構建好的軟體,即使你正在非UNIX平台上開發。
工程學和設計學的每一個分枝都有技術文化。在很多形形色色工程學中,對於從業者的教育,該領域的非成文的傳統和正式的手冊、教科書具有同等的重要性(並且,隨著經驗的增長,經常是比其更重要)。高級的工程師把這些只通過(如同佛教徒所做的)「特殊的傳播,而非手稿」方式傳遞給他們後輩的隱性知識顯彰在大量的有形體上。
軟體工程是這些規則中一個普遍例外。技術變化的太快,軟體環境日新月異,技術文化脆弱且短暫。當然,也不全是這樣。較少數的軟體技術被證明是有足夠的生命力來發展出強大的技術文化、特色的藝術,和在工程師中代代相傳的關聯的設計哲學。
UNIX文化就是其中的一個。或者說,在21世紀的INTERNET文化是可被論證的另一個。自從80年代早期,隨著這兩者的成長,兩者變得愈加難以分開,並且在本書中,我們也不會試著做這樣的特殊努力。
n UNIX的生命力
UNIX誕生於1969年,從那時到現在它以被流水線式的生產使用。整個時間跨越了計算機工業標準的好幾個地質時期-比PC或工作站或微處理器,乃至視頻顯示終端,和具有同一時代的第一個半導體存儲器的歷史都要悠久。今天,在所有的分時系統中,只有IBM的VM/CMS可以聲稱能有較長時間存在下去,並且UNIX機器已經提供了幾十萬倍還多的運行時數。事實上,UNIX可能提供了比所有的分時系統加在一起所提供的計算還要多。
UNIX已經被認為可以在各種各樣的機器上使用,比起其它任何操作系統所聲稱的都多。從超級計算機到手持和嵌入式網路設備,從工作站到伺服器和過程式控制制系統和微機,UNIX大概預料到了的更多體系結構和更多零散的硬體,比任何3個其它操作系統加起來還要多。
UNIX已經支持壓倒性的寬廣使用領域。科研用工具、作為技術上的定製應用程序的友好主機,商業上暢銷的商務軟體的支撐平台,INTERNET的關鍵構成技術,沒有任何其它系統能夠同時在這些領域取得輝煌。
自從UNIX初期開始,每年都有這樣自信的預言,UNIX會消亡或是被其它操作系統所排擠。然而UNIX,以它當今化身為Linux ,BSD ,Solaris ,MacOS X和半打其它形式的變體,在今天看起來,比以往任何時候都強大。
Robert Metcalf(Ethernet的發明者)說,如果有什麼東西進步取代了Ethernet,那麼它將是所謂的「Ethernet」,出於這個原因,Ethernet不會消亡。UNIX已經經歷了好幾次這樣的轉變
至少UNIX核心技術之一-C語言-已經廣泛的移植到了其它地方。事實上,C作為系統編程中普遍使用的通用語言,很難想像在做軟體工程過程中而不被用到。為了連接應用程序,UNIX也引入了現在以被廣泛使用的帶有目錄節點的樹型文件域名空間和為連接程序的管道。
UNIX的生命力和適應性簡直是令人吃驚的。其它的技術象蜉蝣一樣來來往往。機器在處理能力上增加了上千倍,語言在變異,工業慣例經歷了數次的革命。然而,UNIX依然身處其中,依然在產出,依然在付帳,依然衷心的服務於這個行星上很多最優秀和聰明的的軟體技術人員。
在計算,和相應軟體開發速度方面的指數摩爾曲線的推理之一就是:每過18個月,一個人已有知識的一半就會過時。UNIX沒有廢止這種現象,而是在容納它這方面做了很好的工作。UNIX有基本不變的根底-語言,系統調用,工具使用,事實上,這些能保持好多年不變,甚至是幾十年。在其它方面很難預言什麼將是穩定的。甚至整個操作系統周期性的廢棄。在UNIX下,短暫的和持久性的知識之間有著清晰明顯的區別,並且一個人提早知道的(有大約90%的確定性)各類知識,當它去學的時候,這些知識很可能到期了。UNIX命令是如此的忠誠。
UNIX的穩定性和成就的大部分毫無疑問是歸功於其固有的強壯,歸功於Ken Thompson, Dennis Ritchie, Brian Kernighan, Doug McIlroy, Rob Pike和其他一些早期UNIX開發者在開始時候就為將來做打算的設計決策。歸功於一遍又一遍的被證明是合理的決定。但正是同樣的歸功於在早期圍繞UNIX發展起來的設計哲學,編程藝術和技術文化。自從那時候起,這個和UNIX共生的傳統就在不斷的,成功的進行自我繁殖,複製。
注【4】:事實上,乙太網已經被具有同樣名字的不同技術取代了2次。一次是當同軸電纜被雙絞線取代的時候,第二次是當千兆乙太網流行起來的時候。
n UNIX文化反面學術例子
對於那些已經喜歡上UNIX,和或許已經喜歡上它技術歷史學家,UNIX的穩定性和它的技術文化無疑是最吸引人的地方。但是,作為多用途的分時系統的最初的UNIX程序,是為那些被個人工作站擊敗而迅速消失在歷史煙河中的中型和大型計算機所開發的。於是,在被微軟統制的主流商業桌面電腦市場領域中,有一定的懷疑餘地認為它將不會不斷的取得成功。
外行者經常打發UNIX當作學術上的玩具或是黑客玩耍的沙盒。一個被眾所周知的爭論,《UNIX痛恨者手冊》,跟隨的反對路線幾乎和UNIX本身被記載的一樣久,它的熱愛者作為一群反常信仰的信徒和失敗者而脫離出來。AT&T, Sun, Novell和其它一些商業廠商和標準化組織在給UNIX定位和市場推廣方面都接二連三的犯下了很多大的錯誤,當然,這些都已經成為傳說了。
UNIX看起來一直在搖搖欲墜瀕於普及性好久了,以至於甚至來自於UNIX世界的內部都產生了它決不會實際上達到那裡的懷疑。一些生性好疑的局外評論員的結論認為:也許UNIX太有用了而不會走向消亡,但是也會因為太難以使用而無法逃出密室;一個永久的小生境的操作系統。
LINUX和其它開源UNIX的興起(例如現代各種BSD變體),在挫敗懷疑者方面,比其它任何方面都要重要。UNIX的文化證明是太重要了,甚至10幾年廠商的管理不善也不能將其扼殺。今天,UNIX社區本身已經控制了UNIX的技術和市場,並正在迅速地、顯著地解決著UNIX的各種問題(在這方面,我們將在第20章更詳細的檢查)。
n UNIX做錯了什麼
從1969年以來,就設計方面來說,認為UNIX中那一項設計是勿庸置疑的錯誤是非常困難的。到是也有一些普遍認為有問題的設計,但是它們中每一項在至今仍是倍受爭議的,這些爭議不僅僅是在UNIX愛好者之間,那些考慮和設計操作系統的廣大社區的人們也加入其中。
UNIX上的文件在位元組級別上沒有進一步的結構。文件刪除是不可以恢復的。UNIX安全模型被論證認為太原始。作業控制也很糟糕。對一些東西有太多的不同命名方式。持有的文件系統從肯本上就可能是一個錯誤的選擇。我們將在第20章討論這些技術問題。
但是,對於UNIX來說,大部分經久不衰的的爭論多半是第一次被X windowing系統設計者顯示提出來的哲學中最有特殊的一部分的推論。 X力爭提供一種機制,而不是策略,支持圖形操作系統所提供的非常通用的裝置,並且推遲關於工具包和所見即所得界面的決定到應用程序級。UNIX的其它系統級別的服務顯現出了類似的趨勢。關於行為的最終的選擇儘可能的向用戶有利的方面推動。UNIX用戶可以在多種SHELL中進行選擇。UNIX程序通常提供許多行為選項和令人喜歡的精心製作的偏愛便利。
這種趨勢反映了主要為技術用戶所設計的操作系統的傳統,並且隨之而發生的信仰是:用戶比操作系統設計者更了解他們自己真正需要的是什麼。
這個原則是被貝爾實驗室的Dick Hamming穩固的建立的,當在計算機即稀有又昂貴的50年代的時候他就堅持這一點。那個年代開放式計算站是必要的,在那裡顧客們自己寫程序,因為:用錯誤的方法解決正確的問題總比用正確的方法去解決錯誤的問題要好。
但是,這種非策略機制的方法的代價是,當用戶能設置策略的時候,他必須設置策略。一些非技術性的終端用戶發現UNIX豐富的選項和介面風格壓倒性和倒退了系統,至少提供給了他們偽簡單性。
從段時期來看,UNIX放入主義方式也許失去了一定數量的非技術用戶。然而,從長期來看,它可能化劣勢為優勢,因為策略趨向於短生命線,而機制則相反。今天在界面所見即所得方面的風格太常見了,以至於堵死了將來的進化之路。(就像使用過時的X工具包的人所將要告訴你的感受的那樣)。在競爭對手更多的捆綁策略或界面選擇淡出視線以後,於是反面的反面是:「機制,而不是策略」也許會讓UNIX重新獲得新生。
注【5】:是的,Hamming 的「Hamming 距離」和「Hamming 碼」
注【6】:Jim Gettys,X的架構師之一(也是這本書的貢獻者),在《The Two-Edged Sword 》一文中,對於X的放任政策的風格向將來執行下去會有怎樣的結果有很深的冥想。這篇短文,對於它的細節上的提議和對於its expression of the Unix mindset,都是非常值得讀的。
n UNIX做對了什麼
最近LINUX爆炸式的增長,和INTERNET漸增的重要性,給了我們很好的理由支持,那些懷疑論者的例子是錯誤的。但是,即使懷疑論者的估計是正確的,UNIX文化也是值得學習的,UNIX和它周圍的文化中的一些東西,比起其它任何競爭對手有明顯的優勢。
開源軟體:
儘管在1998年以前,術語「open source」和開源的定義沒有被發明,但是,自從UNIX誕生以來,具有同等意義的自由的共享源代碼的開發就是UNIX文化的關鍵特性。
對於AT&T的原始UNIX的,和主要變種的伯力克UNIX頭十年中,通常是帶著源碼發布的。這就使大量的其它好的事情尾隨而至。
跨平台可移植性和開放標準:
跨越不同種類的計算機、廠商和特殊用途的硬體,能提供一致的,文檔支持的應用程序編程介面(API),在這方面,UNIX仍然是唯一的操作系統。它是唯一的操作系統,能夠支持從嵌入式晶元和手持設備,直到桌面機器,到伺服器,對於特殊目的的大型數字計算設備和資料庫後端更是自始至終的支持。
在現存的API中,UNIX的API是最接近寫真正的可移植軟體的硬體無關標準。不出意外的話,IEEE原來稱作的Portable Operating System Standard,取了它們的首字母加上一個快速取得的後綴就形成了POSIX。對於這樣一個標準,和UNIX具有同等價值的API是唯一的可信模型。
一些其它操作系統上,只有二進位的可執行程序和它們的出生環境一起消亡了,但是UNIX原始資料是永遠存在的。一個給定的UNIX技術的文化至少能閃光和維持它們數十年,或是永遠。
INTERNET和WWW:
美國國防部簽訂了TCP/IP協議棧第一份產品的合同,這有助於UNIX開發組織,因為那個時候UNIX大量的開放源碼正在遭到置疑。除了TCP/IP,UNIX已經成為INTERNET服務提供商不可或缺的核心技術之一。自從80年代中期,家用操作系統TOPS退出歷史舞台後,大部分的INTERNET伺服器都依賴於UNIX。
微軟可怕的營銷策略已經動搖了UNIX在INTERNET世界中的霸主地位。然後,從TOPS-10進化而來的TCP/IP規範,在理論上是和UNIX背道而馳的,嘗試使這樣規範在其它操作系統上工作會遭受不兼容性、不穩定性和很多bug的困擾。這些理論和規範任何人都可以獲得,但是工程習慣於僅在UNIX的世界中使它們穩固和真實運作。
在80年代早期,INTERNET技術上的文化和UNIX文化開始融合,並且現在已經是不可分割的共同體。INTERNET最現代化的外觀,WWW的設計,應該象感謝它的祖先ARPANET一樣感謝UNIX。特別的,對於WEB如此重要的統一資源定位的概念(URL),是UNIX各處統一的文件命名思想的范化。
開源組織:
最初圍繞著早期UNIX開源發布而形成的組織從來沒有消失-在90年代早期INTERNET爆炸性大增長以後,它新吸引入了家用機器上整個新一代熱心的黑客。
今天,對於各種各樣的軟體開發來說,該組織都是強有力的支持團體。在UNIX世界裡,有很多高質量的開源開發工具。開源UNIX程序通常等於,並且經常優於,他們自己擁有的具有同樣意義的程序。帶有完整工具包和基本應用程序套件的整個操作系統,可以在互聯網上免費的獲得。為什麼抓來的代碼,這時你能改編,重用,循環利用,並且節約你自己工作的90%呢?
代碼共享的傳統高度依賴於關於怎樣協助開發和重用,這樣難的的專業技術。並且不是依據抽象的理論,而是通過大量的工程實踐-不明顯的設計規則,允許功能程序不儘是作為孤立的一次性解決辦法,而是作為一個工具包的增強部分。這本書的主要目的是闡明這些規則。
今天,萌芽開源運動正在帶來新的活力,新的技術上的方法,和整個聰明年輕的一代程序員進入UNIX的傳統。包括LINUX操作系統,和與之共生的象APACHE和Mozilla這樣的開源工程,使得UNIX傳統的主流可見性和成功達到了一個空前的程度。開源運動似乎處在贏得將來計算基礎結構定義的標書的邊緣,並且該基礎結構的核心將是正在INTERNET上運行的UNIX機器。
自始至終的向下伸縮性:
大多數被吹捧為比UNIX更現代或更用戶友好的操作系統通過鎖定用戶和開發者進入一個界面的策略,和寧願狹隘和剛性也煞費苦心提供的應用程序編程介面,達到了它們繁華的外表。在這樣的系統上,設計者有預期的任務非常簡單-但是那些沒有預期的任務通常是難以忍受的或是非常痛苦的。
另一方面,UNIX有深入的伸縮性。UNIX提供許多種方式把程序膠合在一起,這意味著它的基本工具包的組件能夠合併產生單獨工具包部分的設計者從未預期到的非常有用的價值。
UNIX的編程介面的多種風格的支持(經常被看做是弱點,因為它增加了對於終端用戶來說系統感覺上的複雜度)也有利於伸縮性。沒有想成為數據管道中簡單一片的程序被強制承擔在精心製作的GUI之上的複雜性。
UNIX傳統把重點放在強調保持程序介面相對較小、乾淨和正交上-產生深入的可伸縮性的另一個特點。簡單的事情是簡單的和困難的事情至少儘可能簡單,貫串這整個UNIX系統。
UNIX是黑客的娛樂:
忙於UNIX先機技術的具有教皇地位的那些人常常可能肯本不提它的非常有重要性的力量,這一類人放下了他們所有的浮名。UNIX是黑客的娛樂。
UNIX的擁護者看起來不時的就對黑客的貢獻而沒有答謝表示慚愧,儘管承認他們的娛樂可能不知何故的損害了他們的合法性。但是和UNIX打交道和為其開發是一種娛樂,是一個事實,並且,永遠都會是這樣。
沒有多少任何人都永遠用「樂趣」來形容的操作系統。事實上,在大部分其它環境下的開發被形象的比喻為摩擦和分娩。
to kicking a dead whale down the beach。經常聽到的親切的形容詞是「可以忍受」或「不是非常痛苦」這樣一類的。在UNIX的世界裡,恰好相反,操作系統獎賞努力,而不是阻止它。在UNIX下經常編程的人達成共識,它不是作為一個用棍棒,使你通過全力的努力按照它的命令去做的敵手,而寧願作為實踐的積極的幫手。
這有真正的經濟意義。在UNIX歷史的早期,娛樂因素髮起了一個道德圈。人們喜歡UNIX,因此為它編寫更多的程序,使它更好用。今天,人們把構建完整的,產品級質量的開源UNIX系統當作是一個業餘愛好。為了明白這是多麼有價值,當你最近聽說任何人科隆了OS/360 ,或是 VAX VMS 或是 Microsoft Windows僅僅是為了娛樂,請捫心自問。
或者,從設計的角度上來看,娛樂因素是微不足道的。作為程序員和開發者的類型的人,當他們不得不做出努力來做對於他們來說有挑戰,但是剛好在他們能力之內的任務的時候,他們有「娛樂」。「娛樂」因此是高效率的象徵。痛苦的開發環境浪費了努力和創造力。他們在時間,金錢,和機會上付出了巨大的隱式代價。
如果UNIX在所有其它方面是一個失敗者,在開發中保持娛樂的方面,UNIX的工程文化將是值得研究的-因為娛樂是開發者高效、有效、多產的象徵。
UNIX教訓能應用在其它地方:
當倡導我們現在想當然的系統特徵的時候,UNIX程序員已經積累了數十年的經驗。即使非UNIX程序員也能從UNIX的經歷學習中受益。因為UNIX使得實施好的設計原則和好的開發方式相對要容易,它是學習它們的最佳場所。
其它操作系統寧願採用更困難的好的習慣做法,儘管雖然一些UNIX文化的教訓是可以移植的。許多的UNIX代碼能直接移植到任何支持ANSI C的操作系統上。
注【7】:其它操作系統一般有拷貝的或克隆的UNIX TCP/IP實現。It is their loss that they have not generally adopted the robust tradition of peer review that goes with it, exemplified by documents like RFC 1025 (TCP and IP Bake Off).
注【8】:這句話最初是由Stephen C對IBM MVS TSO 設備的宣傳,或許作為YACC的作者更被廣為人知。
n UNIX哲學要素
UNIX哲學起源於Ken Thompson的關於怎樣設計一個具有清晰的服務介面的小型的,但是有能力的操作系統的早期思考。關於在Thompson的設計範圍外,如何獲得最大的槓桿作用已經成長為UNIX文化學問上的東西。它全神貫注於沿著這條路上的許多來源的教訓。
UNIX哲學不是流於形式上的設計方法。它不是從那些號稱可以產生理論上完美的軟體的高深莫測的計算機科學理論上抄襲下來的。它既不是在非常短的一個最終期限內,可以從動機不明的、糟糕的管理,和待遇很低的程序員那裡魔法般的榨取出有創新的,但是可靠的軟體的方法,也不是行政管理人員長期的幻想。
UNIX哲學是從底向上的,而不是至頂向下的。它是注重時效的和以經驗為基礎的。它不出現在官方的方法和標準里,而是在隱式的半自悟式的知識里,這是UNIX文化傳播的專門技術。它鼓勵用一定比例理性的懷疑論和理性的(經常是具有破壞性的)幽默來表達。
Doug McIlroy,UNIX管道的發明者和UNIX傳統的奠基人之一,曾經說過:
1、是每一個程序只做好一件事情。如果要做新的工作,就構建新的程序,而不是通過增加新的特性而使老的程序變得複雜。
2、預期每一個程序的輸出都是另一個的輸入,即使是未知的程序。不要把輸出和額外的信息混和在一起。避免嚴格的列形的或是2進位的輸入格式。不用堅決要求互動式輸入。
3、設計和構建軟體,甚至是操作系統,應該是及早經過驗證的,理想情況下是在幾周之內完成這件事。毫不猶豫的拋棄笨拙的部分和重新構建它們。
4、使用不需要什麼技能的偏愛的工具來減輕任務的編寫,即使你不得不饒彎路來構建這些工具,並且預計到當你使用完它們後就會拋棄它們。
他最好這樣總結:
編寫只做一件事情,並且要做好的程序;編寫可以在一起工作的程序,編寫處理文本流的程序,因為這是通用的介面。這就是UNIX哲學。
Rob Pike,他是精通C語言的大師之一。在《Notes on C Programming》一書中提出了略微不同的觀點:
1、你無法判定你的程序那裡將會耗時。瓶頸出現在令人吃驚的地方,因此直到你已經證實瓶頸在那裡的時候,不要試圖做第二次猜測和陷入盲目的提升速度之中。
2、可度量的。直到你有一個標準之前,不要進行速度的調協,並且除非代碼的一部分侵佔了大部分資源,否則都不要這樣做。
3、Fancy演算法在N很小的時候速度較慢,並且N通常是小的。Fancy演算法有很大的常數。直到你知道N經常將是較大的之前,不要使用Fancy演算法。
4、Fancy演算法比簡單的演算法容易犯錯誤,並且它們是比較難實現的。使用簡單的演算法和數據結構。
5、數據至上。如果你已經選擇了正確的數據結構,並且組織的很好,那麼演算法將幾乎經常是不言而喻的。數據結構,不是演算法,是程序的核心。
6、沒有規則6。
Ken Thompson,他設計並實現了最初的UNIX,用佛教創始人有價值的格言式的座右銘來加強了Pike的第四條規則:
當拿不準的時候,使用蠻力。
大部分隱式的UNIX哲學不是這些前輩所說的,而是他們所做的和UNIX自身建立的例子。從整體上看,我們能夠抽象出下面這些觀點:
1、模塊性原則:寫簡單的,通過乾淨的介面可被連接的部件;
2、清楚原則:清楚要比小聰明好。
3、合併原則:設計能被其它程序連接的程序。
4、分離原則:從機制分離從策略,從實現分離出介面。
5、簡單原則:設計要簡單;只有當你需要的時候,增加複雜性;
6、節儉原則:只有當被證實是清晰,其它什麼也不做的時候,才寫大的程序
7、透明原則:為使檢查和調試明顯更容易而設計
8、健壯性原則:健壯性是透明和簡單的追隨者
9、表現原則:把知識整理成資料,於是程序邏輯能變得易理解和精力充沛的。
10、 最小意外原則:在介面設計中,總是做最小意外事情
11、 沉默原則:當一個程序令人吃驚什麼也不說的時候,他應該就是什麼也不說
12、 修補補救:當你必須失敗的時候,儘可能快的吵鬧地失敗
13、 經濟原則:程序員的時間是寶貴的;優先機器時間節約它。
14、 產生原則:避免手工堆砌;當你可能的時候,編寫可以寫程序的程序;
15、 優化原則:在雕琢之前先有原型;在你優化它之前,先讓他可以運行;
16、 差異原則:懷疑所有聲稱的「唯一真理「
17、 可擴展原則:為將來做設計,因為它可能比你認為來的要快
如果你是UNIX新手,那麼這些原則相當值得思考。軟體工程課本里介紹了它們中的大部分。但是大部分操作系統缺乏正確的工具和傳統來把它們轉換成實踐,因此大部分程序員不能帶有任何的連貫性使用它們。他們已經接受了生硬的工具,糟糕的設計,過渡工作,和習以為常的代碼膨脹-這些另UNIX的愛好者所驚訝的煩惱。
1、模塊性原則:寫簡單的,通過乾淨的介面可被連接的部件;
正如Brian Kernighan曾經說過:「控制複雜性是計算機編程的本質」。調試比開發時間多,並且getting a working system out the door is usually less a result of brilliant design than it is of managing not to trip over your own feet too many times.
彙編,編譯器,流程圖,過程化編程,結構化編程,「人工智慧」,第四代語言,面向對象,不計其數的曾經當作可以做為解決該問題而被吹捧和銷售的軟體開發方法學。所有的這些都已經失敗了,如果要說成功的話,那麼它們逐步增加了程序負責的一般級別到了幾乎超出了人類所能理解的範圍,這是唯一的成功。正如Fred Brooks的那句經典的話:沒有銀彈。
編寫不流於形式的複雜程序的唯一方法是控制整體複雜度下降-通過定義明確的介面進行連接的多個簡單的部件進行構建,這些大部分問題是局部化的,並且你有一些希望在不破壞整體的情況下而濃縮其中的一部分。
2、清楚原則:清楚要比小聰明好
因為維護是如此的重要和如此的昂貴,寫程序好像最重要的就是交流,但這裡的交流不是執行它們的計算機,而是將來閱讀和維護程序的人(包括你自己)。
在UNIX傳統里,超越這個建議的暗示就是給你的代碼加上注釋。好的UNIX實踐也擁抱為將來的可維護性的演算法和實現所做出的選擇-不僅僅是複雜的代碼容易孳生bug,也因為將來的維護者會很難理解這些複雜的代碼。
另一方面,優美而清晰的代碼,較少可能出現問題-並且更可能被下一個不得不修改它的人所立即理解。這是非常重要的,特別是當下一個人是比你晚出道好幾年的。
決不要努力的去譯解晦澀的代碼3次。一次可能僥倖成功了-但是你發現你不得不第二次去理解它-因為第一次已經是很久以前的事情了,並且你已經忘記了它-那麼是時候給代碼加上注釋了,以便第三次能相對來說少一點痛苦。
3、合併原則:設計能被其它程序連接的程序。
如果你的程序沒有一個能互相交換的,那麼編寫成塊的過於複雜的程序是很難避免的。
UNIX傳統強烈建議編寫那些編寫那些讀寫簡單的,文本的,面向流底,設備無關格式底程序。在經典的UNIX下,許多程序儘可能被編寫做為一個簡單的過濾器,它們以簡單的文本流做為輸入,並且以簡單的文筆流作為輸出。
儘管象神話一樣流行,這個實踐被喜愛,不是因為UNIX程序員痛恨圖形用戶介面。這是因為如果你不寫接收和輸出簡單文本流的程序,那麼把這些程序串接在一起是非常困難的。
文本流對於UNIX工具來說就象在面向對象框架中消息對於對象一樣。文本流介面的簡單性加強了工具集的封裝。許多精心製作的進程間通訊的形式,例如遠程過程調用,表現出了是程序彼此陷入太深的趨勢。
為了是程序可以組合,是它們保持獨立。文本流一端的程序應該儘可能的少的關於另一端的程序。它應該是很容易的實現替換一端而不用干預另一端。
GUI可以是非常好的東西。依據任何合理的想法,複雜的二進位格式有時候是不可避免的。但是在寫GUI程序以前,思考一下你的程序的靈活的交換式部分是否能單獨的分離出來,負荷工作的演算法分離形成另一部分,然後用簡單的命令流或是簡單的協議把這兩部分連接起來,這樣的思考是明智的。在設計一個向周圍傳輸數據的狡猾的二進位格式之前,是值得做試驗的,看看你是否可以採用簡單的文本的格式進行工作,並且採用通用目的的工具進行數據流的處理,是否可以接受一點解析上的負荷代價。
當一個序列化,協議式的介面不是為專為一個應用而寫的時候,正確的UNIX設計是至少儘可能多的把應用程序的最基礎的部分組織入定義明確的庫中。這就打開了應用程序可以通過連接調用的可能性,或者多樣的介面可以進行膠合而處理不同的任務。
4、分離原則:從機制分離從策略,從實現分離出介面。
在我們的UNIX做錯了什麼的討論中,我們評說,X的設計者做了實現「機制,而不是策略」的基本決定-是X成為通用的圖形引擎,把關於用戶介面的決定留給了工具包和系統的其它級別。通過指出策略和機制在不同時間尺度上的變異趨勢,和策略的改變比機制快的多,我們被證明是對的。所見即所得的GUI工具包的時尚可以來來往往,但是光柵的操作和合成是永恆的。
因此,把策略和機制硬性的合在一起有兩方面的壞的影響:它使得策略是剛性的和很難改變響應用戶的需求,並且它意味著如果試圖改變策略,那麼就會有強烈的趨勢動搖機制。
另一方面,通過把它們兩個分開,我們是可能在不破壞機制的前提下試驗策略。我們也使得為機制編寫測試非常容易。
在GUI上下文之外,這個設計原則有很廣的應用。通常來說,它暗示我們應該尋找把介面從引擎分開的方法。
有效的分離方法之一是,例如,把你的程序寫成供嵌入式腳本語言驅動的C服務常式庫,用腳本語言而不是C語言來寫程序的控制邏輯。這個模式的經典例子是Emacs編輯器,它使用一種嵌入的LISP解釋程序去控制原本用C編寫的編輯。我們在第11章討論這種設計風格。
另一種方法是,把你的程序拆分成通過在SOCKET之上用專門的應用協議進行通訊的前端和後端程序協作處理。前端實現策略,後端是機制。這一對的整體複雜度要遠遠低於實現同樣功能的整體上的單個程序,降低了出BUG的弱點和解約了生命周期的費用。
5、簡單原則:設計要簡單;只有當你需要的時候,增加複雜性;
許多壓力程序趨向於更複雜(並且因此更昂貴和臭蟲成災)。一類這樣的壓力是技術上的大男子主義,那些聰明的程序員以他們能夠處理複雜和迷惑的抽象的能力而引以為豪。他們經常和同類人比賽,看誰能夠構建最難以理解和漂亮的複雜事物。和剛才的常常一樣,他們的設計能力超出了他們實現和調試的能力,和昂貴的失敗結果。
「難以理解的和優美的複雜事物」的觀念幾乎是一個矛盾的修飾手法。彼此為「簡單和優美」而競爭的UNIX程序員是真的值得尊敬的-這一點是這些規則暗示,但是顯示的提出來是非常有價值的。
甚至更多(至少在商業軟體的世界裡)的額外複雜性來源於那些基於一時的市場的項目要求,而不是客戶想要的真正的東西或是能實踐發布的軟體。許多好的設計在市場的一堆「功能列表」下被扼殺-那些功能,經常是客戶永遠也不會用到的。隨之是惡性循環的操作。競爭者認為它不得不加入更多的浮華的特性來與浮華的特性競爭。不久以後,大塊膨脹的是工業標準,並且每一個人都在使用巨大的,甚至是那些開發人員都不能解決的臭蟲成災的程序。
不論那一種方式,最終每一個人都迷失了。
避免這些問題的唯一辦法是鼓勵那些以小為美而著稱,積極抵制膨脹和複雜的軟體文化:把高價值放到一個簡單解決方案上的工程傳統,它期待分裂程序系統為多個小的相互協助的片斷的方法,並且它自發地努力同用大量的浮華特性粉飾的程序(或者,更壞的是,圍繞浮華特性去設計的程序)進行鬥爭。
那將會是更像UNIX的文化。
6、節儉原則:只有當被證實是清晰,其它什麼也不做的時候,才寫大的程序
這裡的「大」有代碼體積龐大和內在複雜型兩方面含義。允許程序變大有損於可維護性。因為人們很不情願扔掉很多工作產生的可見的成果物,大的程序引起過度的投資和接近失敗或不理想的。(我們將在第11章用更多的細節來檢驗軟體正確的大小問題)
7、透明原則:為使檢查和調試明顯更容易而設計
因為調試經常在開發的1/3或是更多的時間,早期做的有利於調試的工作是一件非常好的投資。通常使調試輕鬆的有效方法是為透明和可發現性而設計。
當你看一個透明的軟體系統的時候,你能理解明白它正在做什麼和怎麼做的。當程序有便利的手段監視和顯示它的內部狀態,因此你的程序不僅是功能良好的,而且功能良好是可見的,這就是可顯示性。
貫串整個項目,為這些品質而設計都將會有暗示。最小程度上,它暗示了調試選項不應該是事後最小限度考慮。更合適的是,在一開始它們就應該被設計-從這個角度上看,程序即能證明自身的正確性,也能夠向將來的開發者傳達了,原來的開發者解決問題的思維模型。
對於一個要證明自身正確性的程序,需要使用十分簡單的輸入和輸出格式,以便於容易檢查在合法性輸入和正確的輸出之間的正確關係。
為透明性和可見性的面向對象設計,也鼓勵那些容易被其它程序員維護的簡單介面-特別是,測試和監控裝置和調試腳本。
8、健壯性原則:健壯性是透明和簡單的追隨者
當在超出設計者的保證的非預期條件下,和正常情況下的執行效率都很好的時候,該軟體被稱為健壯的。
大部分軟體是脆弱的和臭蟲成災的,因為大部分程序對於人腦來說太複雜而不能立刻理解全部。對於一個貪婪侵佔系統資源的程序,當你不能給出正確的理由,你不能確信它是正確的,並且如果它出現了問題,你不能處理它。
跟隨著獲得健壯的程序的方法是,是使它們的內部對於人類的思考方式來說是簡單的。有兩個主要的方法可以做到這一點:透明和簡單。
為了獲得健壯性,在能容忍不尋常的或是極端的大容量輸入方面的設計也是重要的。牢記合成原則的幫助。輸入由其它程序來產生的軟體對於壓力測試來說,是聲名狼藉的(例如,據說原來的UNIX編譯器需要小的升級來很好的處理YACC的輸出。)。這些棘手的情況對於人類來說,看起來是無用的。例如,接受空的連表/字元串/等等,甚至在人類簡直不或從不提供空的字元串的地方,避免象產生機械的輸入這樣的情形的不得不需要進行的特殊案例。
在古怪的輸入下,成為健壯的程序的一條重要的策略是,避免在你的代碼里有特殊的案例。bug經常潛伏在為特殊案例處理的代碼中,和在試圖處理不同的特殊案例的部分中間的交互中。
當你能考慮它,並且能立即明白它所正在表達的,我們評說該軟體是透明的。當所正在表達的對於人腦來說足夠的不複雜,能夠不用過度的勞累就能推論出所有潛在的案例,這就是簡單的。你的程序越擁有這兩種品質,它們將越是健壯的。
模塊性是一個組織它們和是它們較為簡單的方法。有其它的方法為簡單而戰。這裡說得這個就是另外的一個。
9、 表現原則:把知識整理成資料,於是程序邏輯能變得易理解和健壯的。
即使最簡單的程序邏輯對於人類來說也是難以驗證的,但是相當複雜複雜的數據結構還算是易於理解和考慮的。為了明白這一點,比較一個有50個指針節點的圖表和50行流程圖的程序的表示和表現能力。或者,比較一個用轉換表和等價的switch語句的數組初始化。在透明和清晰上的不同是戲劇性的。
數據比程序邏輯更容易駕馭。隨後當你在哪裡看到在數據結構的複雜性和代碼的複雜性之間的選擇的時候,選擇前者。更多的:在進化一個設計的過程中,你應該積極的尋找把複雜性從代碼轉向數據的方法。
UNIX社區沒有發起該見識,但是大量的UNIX代碼顯示了它的影響。特別是,在操作指針方面的C語言技巧,從內核向上的各個級別的代碼都鼓勵可被動態更新的引用結構的使用。Simple pointer chases in such structures frequently do duties that implementations in other languages would instead have to embody in more elaborate procedures.
10、 最小意外原則:在介面設計中,總是做最小意外事情
這個作為最小驚訝原則也是被廣為人知的。
容易使用的程序是那些要求用戶學習較少新知識的程序-或者,換言之,容易使用的程序是那些非常高效的把用戶先前已有的知識連接起來的程序。。
因此,在界面設計中,要避免那些無理由的新鮮事物和過分的小聰明。如果你在寫一個計算器程序,「+」應該總是意外著加法。當設計界面的時候,在界面的感觀上模仿得相似或類似於你的用戶可能熟悉的程序。
注意你的預期的聽眾。他們可能是用戶,他們可能是其它的程序員,或者他們可能是系統管理員。在這些群體中什麼是最小令人驚訝的是不同的。
最小吃驚原則的反面是避免把東西做的淺層次上的相似,但是實際上一點也不同。這是極端背信棄義的,因為外表相似引發了錯誤的預期。把事情明顯的分開比把它們做的幾乎一樣要好。
11、 沉默原則:當一個程序令人吃驚什麼也不說的時候,他應該就是什麼也不說
UNIX上最老的和最持久穩固的設計原則之一就是,當一個程序沒有任何感興趣的或是令人吃驚的東西要說的時候,它應該閉嘴。行為端正的UNIX程序不冒失的做它們的工作,只需要最小限度的忙亂和干預。沉默是金。
「沉默是金」原則最初進化,是因為早期的UNIX視頻顯示。在1969年遲鈍的顯示終端上,每一行額外的輸出都是在用戶時間上的嚴重浪費。限制遠去了,但是為精簡的優秀理由保留了下來。
我認為UNIX程序的精簡是風格的中心特性。當你程序的輸出成為另一個的輸入的時候,它應該是很容易的挑出需要的比特。同時對於人們來說,它是人類因素的必需品-重要的信息不應該和冗長的程序內部行為的信息混和在一起。如果所有的顯示信息都是重要的,那麼重要的信息是很容易找到的。
良好設計的程序對待用戶的注意力和專心作為一種寶貴的和有限的資源,只有當必須的時候才被要求。
(我們將在第11章的結尾用更多的細節討論沉默原則和對於它的理由)
12、 修補補救:當你必須失敗的時候,儘可能快的吵鬧地失敗
軟體在它失敗的方式上應該是透明的,和正常的操作一樣。軟體通過自適應能應付非預期的條件是最好的,但是最壞類型的bug是,那些修復沒有成功,和那些直到很長一段時間以後,否則不會顯示出來的隱蔽的造成訛誤的問題。
因此,寫軟體的時,要寫那些可以儘可能優雅的應付不正確的輸入和它自己的執行錯誤軟體。但是,當無法達到這一點的時候,要以一種使問題的診斷儘可能簡單的方式使它失敗。
Postel的規定也認為:在你所接受方面是不拘泥的,並且在你所發送方面是保守的。「。當時,Postel所說的是網路服務程序,但是這個潛在的思想是非常通用的。設計良好的程序通過從病態形式的輸入中做它們儘可能的識別,來和其它程序合作。它們要麼吵鬧的失敗掉,要麼傳遞嚴格乾淨和正確的數據給鏈中的下一個程序。
然而,也要注意這個警告:原來的HTML文檔被推薦:在你所接收的方面是慷慨的,並且從那時候到現在它使我們很苦惱,因為每一個瀏覽器接收一個不同的規範的超集。規範應該使慷慨的,而不是它們的解釋。
McIlroy認為我們應該為慷慨而設計,而不是修正帶有許可實現的不充分的標準。否則的話,正如他所恰當的指出的那樣,所有的這些太簡單了而不能在陳詞濫調的困境中結束。
13、 經濟原則:程序員的時間是寶貴的;優先機器時間節約它。
在早期的UNIX小型機時代,這個仍然是相當激進的想法。現今,隨著全部的製造業的發展和大部分用戶在廉價的機器的周期上的衝浪,它看起來太明顯而不比說了。
可是,不知何故,實踐似乎完全沒有跟上事實。如果我們把這個座右銘真正的認真的貫串在軟體開發中,大部分程序應該採用象Perl, Tcl, Python, Java, Lisp,甚至shell這樣的,把程序員從內存管理的負擔中解脫出來的高級語言來編寫。
事實上,在UNIX的世界裡這正在發生,儘管在這範圍之外,大部分應用程序廠商看起來似乎仍然採用老式的用C或是C++編碼的UNIX策略。在本書的稍後我們會討論該策略和詳細的權衡。
節省程序員時間的另一個明顯的方法是,教機器做更多的低級別的編程工作,這就導出。。。
14、 產生原則:避免手工堆砌;當你可能的時候,編寫可以寫程序的程序;
人類在力言細節上是聲名狼藉的糟糕。因此,任何一種程序的手工處理都是延期和錯誤的豐富來源。你的程序說明書越是較簡單的分離的,人類設計者越是可能使它正確。生成的代碼幾乎總是比人類手工編寫的要便宜和可靠。
我們都知道這是事實,但是我們經常沒有考慮它的含意。對於人類編寫的那些重複性質的和令人厭煩的高級別語言代碼just as productive a target for a code generator as machine code.。(High-level-language code that『s repetitive and mind-numbing for humans to write is just as productive a target for a code generator as machine code)當它們能提高抽象級別,也就是說當生成器的規格語言比生成的代碼簡單的時候,並且生成的代碼以後不用手工維護的時候,是值得使用代碼生成的。
在UNIX傳統里,對於易傾向於錯誤的細節工作,代碼生成器有大量的使用。解析/詞法 生成器是經典的例子。Makefile生成器和GUI介面構建器是較新的一些。
(我們將在第9章覆蓋這些技術)
15、 優化原則:在雕琢之前先有原型;在你優化它之前,先讓它可以運行;
對於原型第一,最基本的論點是Kernighan 和Plauger的:現在交付功能的90%比永遠無法交付的100%功能要好。至於略微不同的理由,Donald Knuth普及「過早的優化是萬惡之本」的觀察。並且,他是對的。
在瓶頸被知道以前,急於去優化可能是唯一的比特性蔓延更毀壞設計的錯誤。從令人曲解的代碼到不能理解的數據規劃,以損害透明性和簡單性為代價的關於速度或內存或磁碟方面令人迷糊的用法的結果到處都是。它們卵化出了無數的問題和耗費了數百萬的人時-經常,在一些資源的使用上只是獲得邊緣上的收穫比起調試時間來,有非常少的花費。
經常令人不安,過早的局部優化實踐上會阻礙整體最佳化。過早的優化設計的一部分經常防礙變化,這將使得跨越整個設計有大量的更高的代價,因此你會以劣等的性能和及其複雜的代碼而告終。
在UNIX世界裡,有一個長期建立的和非常顯示的傳統,它說:原型,然後是優化。在你優化它之前,先讓它運作。或者:首先讓它工作,然後讓它工作的快。極限編程領袖,Kent Beck,在一個不同的文化里操作,經常放大整個說法到:使它運行,然後使它正確,然後使它快速。
所有的這些引證的延伸都是一樣的:使你的設計正確,在你嘗試調諧之前,採用非優化的,慢的,內存密集的實現。接下來,系統的調諧,尋找那些在局部複雜性上最小可能增加而能獲得最大的性能受益的地方。
對於系統設計來說,原型和優化通用重要-比閱讀很長的規範,判斷一個原型是否做了你所想要的,這是十分容易的。我記得在任何人談論「快速原型」或「敏捷開發」之前,在Bellcore的一個開發經理對抗「需求」文化很多年了。他不願意發出很長的規範;他寧願繫緊一些shell腳本和awk代碼的組合,大概表達什麼是需要的,告訴顧客派遣一些職員到他這裡幾天,於是有顧客過來,並且看他們的職員使用這個原型,並且告訴他是否他們喜歡它。如果他們喜歡,他會說:從現在開始的幾個月里,安如此這般的成本價格,你能讓他具有工業級的強壯。他的估計趨向於精確,but he lost out in the culture to managers who believed that requirements writers should be in control of everything.
使用原型去認識你不必實現的特性有助於性能的優化。你不必去優化你沒有寫的東西。現存的大多數強大的優化工具也許是殺手鐧。
我最多產的時期之一是扔掉1000行代碼-Ken Thompson
(我們將在第12章更深入一些的探究關於相關聯的思想)
16、 差異原則:懷疑所有聲稱的「唯一真理「
即使最好的軟體工具,由於它們的設計者的想像,也是趨向於有限的。沒有人足夠聰明到為每一件東西優化,或是預期他們的軟體可能投放的所有的用戶。剛性的設計,不願意和餘下的世界交談的封閉的軟體是自大的非健康形式。
因此,對於軟體的設計和實現,UNIX傳統包含了一種健康的猜疑「唯一真理」的方法。它擁抱多種語言,開發的可擴展的系統,和無處不在的定製鉤子。
17、 可擴展原則:為將來做設計,因為它可能比你認為來的要快
如果相信其它人所聲稱的「唯一的真理」是不明智的,關於你自己的設計相信他們甚至是愚蠢的。永遠不要假定你有了最終的答案。因此,為你的數據格式和代碼留下增長的空間。否則,你會經常發現你鎖入了不明智的早期選擇,因為當維護向後的兼容性時候,你不能改變它們。
當你設計協議或文件格式的時候,為了可擴展,使它們充分的自描述。總是,總是或者包含一個版本號,或者從自包含,自描述子句合成格式,通過這樣一種方式能夠容易的添加新的句子和去除老的,而不會搞亂可讀格式的代碼。UNIX經驗告訴我們在製作自描述的數據規劃時的微小額外的向上一些,將會以不必破壞事情就能向前進化它們這樣的能力來數千倍的回報給你。
當你寫代碼的時候,組織它以便將來的開發者能夠插入新的功能到框架里,而不必仍棄和重建框架。這個規則不是加入你還不需要的特性的特許。建議寫你的代碼以便以後當你確實需要增加特性的時候是容易的。使結合處可伸縮的,並且在你的代碼里放上「如果你曾經需要。。。」的注釋,你把這種優雅歸公於在你之後那些將會使用和維護你的代碼的人。
在將來你也將會處在這種場合,在更多的最近的項目壓力下維護那些你可能已經忘了一半的代碼了。當你為將來設計的時候,你保留的明智可能是你自己的。
注【9】:
n 在一節課中的UNIX哲學
所有的哲學真正的濃縮為一個鐵一樣的定律,高明的工程師的神聖的「KISS 原則」無處不在。
UNIX給你一個應用KISS原則的優秀的基礎,這本書的剩餘部分將幫助你怎樣學。
n 應用UNIX哲學
這些哲學原則不是只是含糊的一般性。在UNIX的世界裡,它們直接來源於經驗,並且導致了特殊的規定。其中的一些,在上面我們已經揭露了。這裡決不是毫無遺漏的列表「:
任何事情都可以是源和目的無關的過濾器所做的那樣。
如果完全可能,數據流應該是文本格式的(這樣它們能夠被查看和用標準工具過慮)
如果完全可能,資料庫規劃和應用程序協議應該是文本格式的(人類可讀和人類可編輯的)。
複雜的前端(用戶界面)應該從複雜的後端乾淨的分離出來。
只要有可能,在用C編碼之前,採用解釋語言搭鍵原型
編寫每件事物用混和語言要比用單一語言好,如果並且僅僅如果一個人喜歡使程序過於複雜,那麼就使用一種語言
在你所接受的方面是寬鬆的,在你所發出的方面是嚴格的。
當過慮時,決不要扔到你不需要的信息
小是美麗的。Write programs that do as little as is consistent with getting the job done
我們將會看到從它們繼承而來的UNIX設計規則,和指示,在這本書的剩餘部分一遍又一遍的應用。毫不令人吃驚,它們趨向於聚合在其它傳統里來自於軟體工程的非常好的實踐。
n 加之看問題的態度
當你明白正確的事情的時候,去做它-在短期內,這看起來像是更多的工作,但是最後,它是最少努力的途徑。如果你不懂什麼事情是對的,做最小的必須讓工作完成,至少直到你領會什麼是正確的事情之前。
為了正確去做UNIX哲學,你必須忠誠其優點。你必須相信軟體設計是一種工藝,和你所能集聚的所有的智麗,創造力和激情具有同樣的價值。否則,你不會注意過去通往設計和實現簡單的、老套的方式。當你本應該思考的時候,你將會倉促行動編碼。當你本應該無情的簡單化的時候,你粗心的使它變複雜-並且,接下來你會奇怪,為什麼你的代碼膨脹和調試是如此的困難。
為了正確去做UNIX哲學,你必須足夠重視你自己的時間,決不要浪費它。如果有人已經解決一個問題一次,不要讓自尊心或是政見把你吸入又一次解決這個問題中,而不是重用它。同時,不要比你不得不工作的更努力。用巧妙的工作代替,保存額外的努力為當你需要的時候。盡你可能的學習你的工具和自動化每一件事情。
軟體設計和實現應該是一件高興的藝術,一種高級別的遊戲。如果這個看法看起來荒謬的或是含糊的,對於你來說是令人為難的,停下來並且思考一下。問你自己你已經忘記了什麼。為什麼你設計軟體,而不是做其它的事情去賺錢或是打發時間?你一定曾經有過軟體是值得你的激情的想法。
為了正確去做UNIX哲學,你需要有那種態度。你需要注意。你需要扮演。你需要心甘情願的去探索。
我們希望你將會把這種態度帶入這本書的剩餘部分。或者,至少,這本書將會幫助你重新發現它。
推薦閱讀:
※燕子服裝設計館的文件夾【服裝款式】
※這樣子臨摹,練習幾百次都沒有用
※國內設計師山寨「維秘內衣秀」 獸獸高調亮相(圖)
※【大寶】為什麼日本是個值得設計師反覆去旅行的國家(強烈推薦)
※在肚臍上燒水的人