《UNIX编程艺术》--读书笔记
公司入職前安排了讀書環節,后臺開發類推薦了一下書籍:
- C++ Primer
- Unix 環境高級編程(APUE)
- Unix網絡編程 1/2卷(UNP)
- TCP/IP協議詳解
- 深入理解計算機系統
- Effective C++
- UNIX編程藝術
以上書籍之前多多少少都接觸過一些,最近在做畢設方面的事情,想著看本輕松點的書籍,就從圖書館借了本《UNIX編程藝術》。花了一兩周的時間閱讀完此書,全書較多涉及UNIX編程的理念,而不是具體的技術。新手可以學到很多理論上的知識(非常適合拿來裝逼吹水),比如KISS原則、模塊化、透明性、機制與策略分離等,而老手在接觸到這些理論之后再結合自己做過的東西,仔細思考,應該也會有自己一番的體會。所以不論是新手還是老手看此書都會有很多收獲。此書寫于2004年,到現在也有些年頭了,內容舉例可能會有些陳舊,我們在閱讀的時候,也可以想想作者說的現在是否正確。
下面的一些內容是自己閱讀之后做的一些筆記,以供時常拿來翻閱。
I-場景
1. 哲學
- 機制而不是策略,自由放縱注意風格,產生了多樣性。比如Unix應用程序提供很多的行為選項,讓非技術的用戶暈頭轉向,而失去了很多用戶;但是策略相對短壽、機制才會長存,具有很大的靈活性。
- 趣味性是一個峰值效率的標志。對于程序員和開發人員來說,如果完成某項任務所需要付出的努力對他們是個挑戰又恰好還在力所能及的范圍,他們就會覺得很有樂趣。 而UNIX有Hack趣味。
- UNIX哲學是自下而上的,注重實效,鼓勵分清輕重緩急的感覺以及懷疑一切的態度,并鼓勵你以幽默達觀的態度對待這些。
- 簡潔是UNIX程序的核心風格。 UNIX哲學的KISS原則:Keep it simple, stupid!
- 先制作原型,再精雕細琢。優化之前,先確定能用。
2. 歷史–雙流記
本章主要回顧UNIX的歷史,來闡明如今的UNIX文化為什么呈現當前的狀態。 兩個歷史分別是UNIX的起源和歷史以及黑客的起源和歷史
UNIX的起源和歷史
- UNIX的祖輩是CTSS–兼容分時系統,父輩是頗具開拓性的Multics項目(試圖建立一個具備眾多功能的系統);
- 創世紀:1969-1971年,UNIX誕生于貝爾實驗室;
*出埃及記:1971-1980,C語言的加入,其表述方式帶來的可讀性、可改性和可移植性,給UNIX帶來了一定的成功。 - TCP/IP和UNIX內戰:1980-1990, TCP/IP的引入,讓UNIX更加有活力。 然而微軟的興起和AT&T的拆分,讓UNIX進入了煉獄。UNIX的產業化,破壞了UNIX源碼的自由交流,而自由交流卻正是滋養UNIX系統早期的活力。
- 反擊帝國:1991-1995,Linus Torvalds宣布了Linux項目,依賴分布式開發和patch(補丁)工具,加入了Internet和圖形界面,實現了Unix的平價之夢,并拼裝了UNIX的傳統元素。 1995年之后,UNIX的故事就變成了開源運動的故事。
黑客的起源和歷史:1961-1995
- 游戲在校園的林間:1961-1980年,MIT的人工智能實驗室的程序員應該是第一批自稱”hacker“的人;
- 互聯網大融合與自由軟件運動:1981-1991. 1983年,BSD植入了TCP/IP,UNIX文化和ARPANET文化開始融合。RMS創建了GNU項目,致力于編一個完全自由的操作系統。自由軟件術語的提出,讓黑客文化更加有自我意識。
- Linux和實用主義者的應對:1991-1998. Linus Torvalds巧妙地跨越了GPL和反GPL的派別之爭。他利用GNU工具包搭建了自創的Linux內核,用GPL的傳染性質保護它。Torvalds明確的表示他認為自由軟件通常很好,但他偶爾也用專有軟件,他拒絕成為狂熱分子。
開源運動:1998年及之后
- 一個部落的零距離可能來自他們維護的代碼庫,或是一個或多個有著超凡影響力的領導者,或是一門語言,一個開發工具,或是一個特定的軟件許可,或是一種技術標準,或是基礎結構某個部分的管理組織。 1995年之后,Linux扮演了一個特殊的角色:既是社區內多數軟件的統一平臺,又是黑客中最被認可的品牌。整個黑客文化開始凝聚在一個共同目標:推動Linux和集市開發模式向前發展。
- ”開源“背后另一個意圖就是希望將黑客社區的方法以一種更親和市場、更少對抗性的方式介紹給外部世界。
Unix的歷史教訓
- 距開源越近就越繁榮;
- 過度依賴任何一種技術或者商業模式都是錯誤的–相反,保持軟件及其設計傳統的靈活性才是生存之道;
- 別和低價而靈活的方案較勁;
- 真正的專業和奉獻精神,正是我們在屈服于世俗觀念的”合理商業做法“之前的所作所為。
3. 對比:Unix哲學同其他哲學的比較
操作系統的風格元素
- 操作系統的統一性理念。比如UNIX系統的”一切皆文件“模型和由此基礎上建立的管道概念。
- 多任務能力。Unix系統擁有搶先式多任務的能力。 多任務和多用戶不是一回事。
- 協作進程:UNIX的IPC(進程間通信方式)很靈活。
- 內部邊界: Unix是相信程序員的,但是程序員不能破壞其他人的數據,所以Unix設立了內部邊界來防范惡意用戶或者有缺陷的程序。
- 開發的門坎: Unix將編譯器和腳本工具放在默認安裝中,支持了一種跨越眾多機器的玩家開發文化。
操作系統的比較
- 列舉了若干操作系統的優缺點。
種什么籽,得什么果
- 競爭對手的一些缺點: 比如不可移植性、不具備良好的網絡支持能力。
- 而Windows在服務器上的缺陷(安全等原因),使得Linux取得了重大突破。
- 就像造房子一樣,在堅實的地基上修理上層建筑當然要比更換地基而不破壞上層建筑來得容易。
II–設計
4. 模塊化:保持清晰,保持簡潔。
- 早期的Unix程序員擅長模塊化是因為他們被迫如此,如果沒有良好的架構,操作系統就會崩潰。
- 封裝良好的模塊,不會過多向外部披露自身的細節,不會直接調用其他模塊的實現碼,也不會胡亂共享全局數據。
- 具有最佳尺寸的模塊并不意味著代碼有高質量,還得考慮緊湊性和正交性。
- 緊湊性就是一個設計能否裝入人腦。比如一個設計有經驗的用戶不需要操作手冊,這個設計就是緊湊的。
- 正交性是指任何操作均無副作用,每一個動作只改變一件事,不會影響其他。
- 不要重復自身(don’t repeat yourself)。
- 軟件的分層:自頂向下和自底向上。 當自頂向下和自底向上發生沖突時,頂層的應用邏輯和底層的域原語集必須用膠合邏輯層來進行阻抗匹配。 膠合層是個挺討厭的東西,必須盡可能薄。
- OO語言使抽象變得容易,但是過多的層次破壞了透明性,我們很難看清層次,無法理清代碼的運行過程。
- 單個函數與其說是行數計算問題,不如說是內部復雜度性的問題(比如說局部變量太多、代碼存在太多縮進)。
5. 文本化:好協議產生好實踐
- 設計文件格式和應用協議需要考慮的重要方面:互用性、透明性、可擴展性以及經濟型。
- 使用二進制的唯一正當理由:處理大批量的數據集或者關心時間或指令開銷。
- 數據文件元格式有多重不同的元格式,比如DSV、RFC 822、Cookie-Jar、Record-Jar、XML、Windows INI等;
- 應用協議如果是文本格式的,憑肉眼可以很容易地分析,很多事情變得容易。可以看看SMTP、POP3和IMAP三種經典的應用協議。
- 應用協議元格式:盡管網絡帶寬比存儲昂貴許多,需要重視事物處理的經濟性,但是文本格式的透明性和互用性優勢還是十分顯著,大多數設計者還是選擇了采用可讀性更高的文本格式。比如HTTP協議、BEEP協議、XML-RPC/SOAP/Jabber協議。
6.透明性:來點光
在第五章中,討論了數據格式和應用協議進行文本化的重要性,文本化讓透明性和可顯性的品質得到了提升。
* 如果實際上能夠預測到程序行為的全部或大部分情況,這個程序就是透明的;
* 如果程序可以幫助人們建立“做什么、怎樣做”,這個軟件系統就是可顯的。比如對用戶來說,文檔有助于可顯性; 對程序員而言,好的命名規范有助于提高可顯性。
* 要追求代碼的透明,最有效的方法很簡單,就是不要在具體操作的代碼上疊放太多的抽象層。
* 透明的系統在bug發作時,更容易實施恢復措施;同時,透明的系統更容易讓人理解,從而更加方便維護。
7. 多道程序設計
- UNIX最具特點的程序模塊化方法就是將大型程序分解成多個協作進程。
- 多個并發進程除了帶來模塊化的好處之外,另一個原因是為了更強的安全性。
- UNIX IPC:把任務轉給專門程序(shell out)、管道/重定向(管道主要缺點是單向性,命名管道可以作為兩者間的配接器)、從進程、對等進程(臨時文件、信號、套接字、共享內存)。
8. 微型語言:尋找歌唱的樂符
UNIX班有個長期傳統,存在小型的、為專門應用領域特制、大量減少程序行數的語言。 比如無數Unix排版語言(troff/pic)、shell使用程序(awk/sed/dc/bc)和軟件開發工具(make/yacc/lex等)。 微型語言與腳本語言之間的界限都很模糊。
* 一切可計算的問題都可以計算,叫做圖靈完備。
* 明白微型語言在什么時候什么場景下使用。
* 某些情況下,需要我們我們去設計一個微型語言。 首先要盡可能保持微型語言的簡單(復雜度),思考能否通過擴展或者嵌入現有腳本語言來實現自己的微型語言(這是實現命令性語言的正確方法),慎用宏。
9. 生成:提升規格說明的層次
- 數據比程序邏輯更易駕馭,更加直觀,透明性和清晰性方面更勝一籌。
- 數據驅動編程,將代碼和數據結構劃分清楚,在改變程序邏輯時,只要改變數據結構而不用修改代碼。
- 專用代碼的生成,比如用工具生成HTML代碼。盡可能少干活,建設性的懶惰是大師級程序員的基本美德之一。
10.配置:邁出正確的第一步
- 配置在哪里?在下面這些地方,查詢通常按照下面的順序進行,后面的設置會覆蓋前面的設置。
- 運行控制文件,在/etc/目錄下。
- 環境變量,系統環境變量、用戶環境變量。
- 命令行選項
- 上述配置是從最不易改變到最易改變的順序排列的。
11. 接口:Unix環境下的用戶接口設計模式
- 程序的接口就是程序同用戶或者其它程序通信的方法總和。
- 最小立異原則:少來標新立異,是所有接口設計中的通用原則,且并非僅局限于軟件設計。一心不能二用,應該把中心放在接口所屬的任務上。而這個原則,也不應被理解為機械的保守主義。 如有可能,盡量允許用戶將接口功能委派給熟悉的程序來完成,不能委派時就效仿。
- Unix程序中存在豐富的接口風格:面向行的、面向屏幕字符陣列的和基于X的,不同的接口風格,適用于不同的任務。
- 接口的幾種度量標準:簡潔、表現力、易用、透明性和腳本化能力。
- CLI和可視化接口之間的權衡。命令行更具表達力,尤其是針對復雜的任務,同時具有高度的腳本化能力,但是CLI需要費勁地記憶(易用性低),并且透明性也非常低。比如說數據庫的SQL語句和圖形界面操作來說,可以很明顯看出二者之間的差別,同樣命令行的計算器和圖形界面的計算器對比也比較明顯。
- Unix接口設計模式。根據輸入和輸出可以分為這些模式:過濾器模式(比如grep)、Cantrip模式(比如clear)、源模式(不需要輸入,比如ls)、接收器模式(只接收不輸出,例子較少,比如lpr)、編譯器模式(既無標準輸入也無標準輸出,將錯誤消息發送到標準錯誤端)、ed模式(比如ftp、sh)、Roguelike模式(字符陣列比如vi)、“引擎和接口分離”模式(MVC模式作為GUI原型的建議)、CLI服務器模式
- 網頁瀏覽器作為通用前端,無需編寫一個定制的GUI前端。
12. 優化
- 過早優化是萬惡之源
- 不到萬不得已,盡量別去優化一個工作中的系統,而是等上幾個月,期待硬件性能更好。
- 先估量,再優化。通過profiler,去明確瓶頸所在,而profiler本身也是存在工具誤差的。
- 最有效的代碼優化方法是保持代碼短小簡單。目標機器是分層的,將核心的數據結構和指令代碼放在快速緩存。
- 快速處理器的另一個效應是性能經常受限于I/O以及網絡事務的開銷,要盡量避免協議的往返。
- 有三種常規策略來減少延遲:批操作、重疊操作、緩存操作結果。
13. 復雜度:盡可能簡單,但別簡單過了頭
- “Keep it simple, stupid”, 對于簡單的理解其實是很復雜的。
- 復雜度的三個來源:程序員實現的復雜度、顧客和用戶使用的復雜度以及代碼量,關于怎么去折中,是沒有標準答案的。
- 通過五個編輯器的故事,了解編輯器在處理更復雜任務時產生的不同程度的選擇復雜度。
- 吝嗇原則:只有實證了其它方法不通時才寫龐大程序。
III– 工具
14. 語言:C還是非C
- Unix下面存在非常多的語言種類。一是因為Unix廣泛應用于研究和教學平臺,二是因為應用設計和實現語言的合理搭配對生產力有極大促進。
- C語言很厲害、很經濟。但是要求程序員自己完成內存管理,很復雜,并且隨著硬件設備的性能提升,主要瓶頸集中在I/O事件等待、網絡延遲以及緩存列填充等限制上,所以Python、Java等語言慢慢興起。
15. 工具:開發的藝術
本章將介紹Unix下的開發策略–編譯代碼、管理代碼配置、性能分析、調試以及自動完成各種臟活累活。 這一套工具比起IDE更加靈活。
* 編輯器選擇:vi和Emacs
* 專用代碼生成器:yacc、lex
* 自動化編譯:make。 一個笑話:輸入”make love”,輸出是“Don’t know how to make love”。
* 版本控制系統
* 性能分析:gprof
16. 重用:論不要重新發明輪子
- 很多人喜歡自己造輪子,因為庫可能不是透明的、充滿bug,還不如自己來的痛快。
- 隨著開源的提出,我們應當選擇好的輪子,這樣可以節省時間,提高效率。
- 很多開源的網站,如github等。
- 注意一些許可證的問題。
IV–社區
17. 可移植性:軟件可移植性與遵循標準
- 移植性一直是Unix的主要優勢,可移植的戒律往往在架構、接口和實現上施加了一種簡單化的影響,提高了項目成功的幾率也降低了生命周期的維護成本。
- C語言、Unix標準,IETF、RFC標準,一系列的標準和草案讓接口更加規范,從而更方便移植。
- 編程語言的可移植:Java和Python具備良好的可移植性。
- 可移植工具:autoconf來處理移植問題,configure/make/make install來干凈利落的編譯。
- 可移植性需要標準,而開放源代碼同樣給標準化的過程帶來了重要的影響。
18. 文檔:向網絡世界闡述代碼
- 文檔一般分為兩類,所見即所得(word)和標記為中心(XML、markdown),各有優缺點;
- 編寫Unix文檔最佳實踐:信息密度適中、不要過于龐大、也不要省略功能細節和存在的問題、將文檔放在網上。
19. 開放源碼:在Unix新社區中編程
- 開源開發的規則很簡單:源碼公開、盡早發布-經常發布、給貢獻以表揚(物質獎勵或者精神獎勵)
- 與開源工作者協同工作的最佳實踐:版本控制系統(Git、svn等)、良好的代碼注釋、良好的代碼規范和文件命名規范、測試好再發布大媽、良好的交流實踐(郵件列表、網站等)。
- 許可證的邏輯,挑選合適的許可證。
20. 未來:危機與機遇
- 回顧過去,網絡互聯、位圖圖形顯示以及個人計算機這三個特殊的技術變化驅動了Unix設計風格的重大變革。
- 盡管伴隨著許多創新,但所有對這三個技術的響應都保持著Unix的設計準則–模塊化、透明性、機制同策略分離以及之前提到的品質。
- Unix對GUI的支持較弱。Unix的API沒有使用異常(C語言缺乏拋出異常的機制)。
- 從歷史來看,我們只要能夠從錯誤中汲取教訓,文化薪火相傳,Unix是不會輸的。
如果覺得文章不錯,可以打賞點~
總結
以上是生活随笔為你收集整理的《UNIX编程艺术》--读书笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: eXtremeComponents文档
- 下一篇: 面试:第十四章:大厂中高级java程序员