UNIX编程艺术笔记
1.6 UNIX原則
組合原則
簡潔原則
透明性原則
經濟原則
1.7 KISS原則
Keep It Simple,Stupid
1.9 態度也要緊
看到該做的就去做,短期來看似乎是多做了,但從長期來看,這才是最佳捷徑
軟件設計和實現,應該是一門充滿快樂的藝術,一種高水平的游戲
如果有足夠多眼睛的關注,所有bug都無處藏身
Unix要繁榮,就必須采用吸納低價而靈活的方案的訣竅,而不是去反對他們
318-327看完
4 模塊性
軟件有兩種設計方式,一種設計得極其簡潔,沒有看得到的缺陷;一種設計得極其復雜,看不出缺陷,第一種難的多
要編寫復雜軟件又不至于一敗涂地的唯一方法,就是用定義清晰的接口把若干簡單模塊組合起來,這樣多數問題只會出現在局部
4.1 最佳模塊大小
一些最有能力的開發者,一開始總是定義接口,然后編寫簡要注釋,對其進行描述,最后才編寫代碼,因為編寫注釋的過程,就闡明了代碼必須達到的目的
模塊之間通過API接口通信,bug最少的最佳物理行在400到800,關于邏輯行和物理行見下面代碼
a="我是一個物理行" a="""我是一個邏輯行 因為我一條語句便跨越了2個物理行"""4.2 緊湊型和正交性
4.2.1 緊湊性
就是一個設計能否裝進大腦的特性: 用經驗的用戶通常需要操作手冊嗎?如果不需要,那么這個設計就是緊湊的
經驗法則: 記憶的條目數大于七,則不太可能是緊湊的
4.2.2 正交性
任何操作均無副作用,只會改變一件事,而不會影響其他
體現了 單一職權原則? 只做好一件事
正交性代碼更容易復用
Don't repeat youself 當你修改重復部分時,你可能只是修改了一部分而非全部,非常危險
設計最好圍繞一個強核心算法,就比如,grep圍繞正則表達式
完美之道,不在無可增加,而在無可刪減
設計方法有自頂向下和自底向上,unix采用自底向上
為了中和自頂向下和自底向上,我們需要膠合層,類似于分層的api,service,common層
而且膠合層應該盡量的薄,比如C語言,就是硬件和軟件的膠合層,而C語言的風格就是盡量簡潔,功能盡量少
API的入口點盡量不超過七個,簡單的API比復雜API更好
5 文本化:好協議產生好實踐
5.1 文本化的重要性
文本文件/協議優于二進制流文件/協議,因為透明性(用戶易讀懂)
這一章就是一堆文本格式的介紹 RFC 822(電子郵件格式,包括 to,subject,cc等) 和XML
以及Unix文本格式的約定
看到 5.3 應用協議設計。 有點看不下去,好無聊,也看不太懂
看到490頁,5.4應用協議元格式,這章完全講協議啊,不看了
473~490
511~554
第六章
美在計算機科學中的地位,比在其他任何技術中心的地位都重要,因為軟件太復雜了。美是低于復雜的終極武器。
可顯性,僅僅做到不晦澀是不夠的,還必須盡力做到有幫助,比如有意義的函數名稱,或者出現故障時有用的日志信息,比如gcc編譯時,打出來的一堆調試信息,易于幫助我們定位,哪里出了問題
透明性,就是我們一眼就知道,這個機器(或者代碼)在干什么
編寫透明、可顯得系統而節省的精力,將來完全可能就是自己的財富
Window注冊表,所有注冊記錄都儲存在一個大文件中,這是不好的,大文件應該分為多個小文件
策略和機制應該分離,策略,就好比你玩家在游戲中的裝備等數據,而服務器引擎則是機制
Freeciv這個游戲,其配置文件類似于windows注冊表,但是它的配置文件只能由游戲開發者改動,這就避免了用戶改動注冊表,導致的注冊表過長,運行速率過慢。
6.2 為透明性和可顯性而設計
這個設計能行嗎?
別人能讀懂這個設計嗎?
這個設計優雅嗎?
這三個問題不是廢話,優雅不是一種奢侈,這些品質對于減少bug和提高軟件長期維護性是最基本的
6.2.2 透明性之禪
最有效的方法很簡單,就是不要再具體操作的代碼上疊放太多抽象層???? 這也是為什么要有薄膠合層
優秀Unix代碼的簡潔依賴于嚴格自律和高水平技藝
6.2.2 為透明性和可顯性
去欲望,少依戀,如實見
程序調用的最大靜態深度是多少,如果大于四,就要當心
每個API的各個函數調用是否正交? 或者存在太多的特征標志?
是否存在一些順手可用的關鍵數據結構或全局唯一的記錄器
代碼增加了特殊情況還是避免了特殊情況
透明性和避免過度保護
隱藏細節和無法訪問細節有著重要區別(比如debug時能打出隱藏的日志)
良好程序應該有調試和探測開關
6.2.4 透明性和可編輯的表現形式
盡量實現可編輯的文本和二進制格式來回無損轉換
CLI (comand line interface)命令行接口程序
6.2.5 透明性、故障診斷和故障恢復
透明的更容易恢復,也更健壯
6.3 可維護性
如果作者以外的其他人能順利理解和修改軟件,那么軟件就是可維護的
Unix哲學:寧愿拋棄、重建代碼也不愿修補哪些蹩腳的代碼
可維護的兩個重要實踐:
選擇簡單的算法,“拿不準就窮舉”,復雜的算法容易出bug,而且難以維護
還有就是,開發者手冊,簡略描述代碼的關鍵數據結構和算法
7 多道程序設計: 分離進程為獨立的功能
Unix的藝術: 將大型程序分解成多個協作進程
做單件事并做好
shell執行,就是創建多個協作進程,并由管道連接
IPC 進程間通信
7.1
過早優化是萬惡之源
線程不是降低而是提高了全局復雜度,因此,除非萬不得已,盡量避免使用線程
一個程序劃分為多個協程,也更利于安全性,只有較小的協程去執行特權指令
最簡的就是最好的
7.2 Unix IPC方法分類
7.2.1 把任務轉給專門程序
專門程序,其實就是不和其他進程通信,只做自己的事情的程序
7.2.2 管道、重定向和過濾器
管道 就是 做單件事并做好
8 微型語言
8.1 理解語言分類法
結構或用的數據格式文件(比如/etc/passwd) -> 微型語言(比如make) -> js -> Java
??
這些語言一定程度上是解釋器,解釋器范疇越大(即所需要的上下文越少)越通用(越靠右),
8.2.1 sng
png沒有透明性,sng卻能夠將png以json字符串的樣式表現出來,所以具備了透明性
8.2.2 正則表達式
微語言的一種,極其簡練卻很有用
8.2.3 Glade
透明性和簡單性
8.2.4 m4
宏命令: define 'OS' 'operation system'
但是謹慎用宏
8.2.5
圖靈完備機
其實語言就是解釋器?
最小立異原則
必須謹慎使用語法糖,以免造成的晦澀多于幫助
8.2.8 awk
unix下輸入?info gawk,獲取在線文檔
9 生成:提升規格說明的層次
程序員束手無策.... 只有跳脫代碼,直起腰,仔細思考數據才是最好的行動,表達是編程的精髓(????表達力嗎)
人類其實更善于觀察數據,而不是推導控制流程(我覺得,人類是視覺動物)
所以,盡可能的把設計的復雜度從程序代碼轉移到數據,是個好辦法
過程式語言 c,
說明式語言 sql
相對來說編譯時,c更難分分析,sql更容易分析(我覺得,透明性),容易分析的語言,更容易找出錯誤
進行數據驅動編程(和面向對象編程不同)時,需要把代碼和代碼作用的數據結構劃分清除,這樣,在改變程序的邏輯時,只要編輯數據結構而不是代碼,例子:ascii轉譯程序,只需要碼表和map邏輯
9.2 專用代碼的生成
9.2.1 ascii 代碼
assic 用法屏幕,就是作者自己手寫一整個表,然后讓代碼去打印這個表,?這樣的話,要改輸出格式,只要對這個手寫的表進行操作就行啦,NB666
9.2.2 為列表生成HTML代碼
我草,這個真是屌爆了
?而我們可以先把數據按以下格式存儲在文檔中
?然后通過sed,生成對應的html列表
我草,太吊了
所有這些例子的借鑒之處都是一樣的:
盡可能少干活;讓數據塑造代碼;依靠工具;把機制從策略中分離?
建設性的懶惰是大師級程序員的基本美德之一
編碼盡量往上推以最小化常數缺陷密度效應??????
10 配置: 邁出正確的第一步
積硅步,至千里
10.1 什么應是可配置的
什么應是可配置的?? 回答:全部
分離原則: 只要可能,就建立機制而把策略決定權交個用戶
盡量用自動檢測來減少配置開關的數量
讓程序經濟運行時設計者的任務,用戶不應該看到優化開關
考慮一下問題:
能省掉這個功能嗎
能否用無傷大雅的方式改變程序的常規行為,從而無需這個選項?
這個選項是否花哨沒用?
增加一個開/關配置選項,就是使測試量加倍,既然在實踐中從來沒有人完成雙倍測試量,那么實際影響,就是減少了特定配置獲得的測試量
10.2 配置在哪里
1 /ect
2 系統環境變量
3 用戶主目錄(LINUX中的~)的運行控制文件
4 用戶環境變量
5 命令行參數
后者覆蓋前者,越往后范圍越小
10.3 運行控制文件
rc 后綴,代表'運行控制(run control)'
其實運行控制文件和點文件(比如.netrc文件, .profile文件),我的理解就是配置文件,用來決定程序按照何種配置去運行
10.3 命令行選項
-a -ab代表組合-a和-b
10.5.1 從-a到-z的命令行選項
-a -all所有
-b buffer或者batch
-c command 命令(帶參數)
-d debug 調試,偶爾delete
-e execute
-f file 代表輸入文件,輸出文件
-i interactive
-k keep或者kill
-l list
-n number
-o output
-p port?
-q quite
-r recurse遞歸 reverse反向
-z zip啟用壓縮
看到881頁
10.6.1 實例分析fetchmail
提供了了太多的選項,以至于用戶手冊過于復雜
解決辦法就是提供-o選項,然后-o后面的參數視為配置文件的一行信息
10.6.2
11 接口: Unix環境下的用戶接口設計模式
我們所有的知識都來源于我們的感知
與其他程序通訊方式的前瞻性設計,這是什么鬼?????
最小立異原則: 如果可能,盡量允許用戶將接口功能委派給熟悉的程序來完成,比如讓用戶選擇自己的文本編輯器
互助式的接口組合使用
共生和委派策略來提高代碼的復用度,并降低軟件復雜度: 讓用戶選擇自己的代理
如果不能委派,那么就效仿
11.3 接口設計評估
簡潔、表現力、透明、易用、腳本化
簡潔: 可以用操作時間來衡量,鍵盤拼寫比鼠標點擊屏幕字符要快得多
表現力:可以觸發相當廣泛的行為,比如鍵盤可以單鍵,也可以組合鍵
易用性:和用戶要記憶的東西成反比
接口透明度:所見即所得,中間結果也很容易看出來
腳本化: 接口更容易為其他程序所用
自動完成重復任務:
roguelike
更具表達力的語言意味著程序更短,bug更少。
過濾器原則
Postel原則 寬進嚴出
過濾時,不需要的信息也絕不丟棄
過濾時,絕不增加無用數據 比如不遵循格式的日期,空行
Cantrip模式
沒有輸入沒有輸出,啟動時指定條件,具備很強的腳本能力
比如 clear rm命令
比較好的風格,交互用腳本語言寫,然后內部調用cantrip程序
源模式
沒有輸入,輸出只能在啟動條件中控制,比如 ls
類編譯器模式
沒有輸入也沒有輸出,但是報錯時會發信息
,交互性較低,所以比較容易腳本化
引擎和接口分離模式
核心算法與接受用戶命令,顯示結果相分離
比如控制器,視圖,模型
驅動/引擎組合
比如驅動可以是不同的網卡驅動,而網卡作為引擎,只有一個
客戶端/服務器模式
配置者/執行者組合
比如fetchmail和fetchmailconf
fetchmailconf用于GUI?
假脫機和守護進程
守護進程,輪詢作業
CLI服務器模式
其實就是守護進程負責監聽,然后fork進程處理輸入,比如inetd
微語言模式
GUI前端,CLI微型語言后端比如數據庫軟件
11.4 應用Unix接口設計模式
要促進腳本化和管道線能力(參考第 7 章),最好就是盡可能地選擇最簡單的接口設計模式——牽扯環境因素最少、交互最少的模式。
既有腳本方式的接口,又有GUI方式的接口
CGI 瀏覽器內嵌程序,用來接受用戶請求,并處理
沉默原則!
喋喋不休的程序,其他程序很難理解,更難交互
程序每產生一行垃圾,用戶可見的信息就少了一行。
信息內容應該符合最大驚奇原則——僅僅對偏離通常期望的情況詳加說明。
12 優化
過早優化乃萬惡之源
Unix的經驗告訴我們最主要的就是如何知道何時不去優化。其次,最有效的優化往往是優化之外的其它事情,如:清晰干凈的設計。
程序員工具箱中最強大的優化技術就是不做優化。
12.2 先估量,后優化
如果有真憑實據證明應用程序運行緩慢,這時(僅當此時)才可以考慮優
最有效的代碼優化方法就是保持代碼短小簡單
永遠不要將核心數據結構和時間關鍵循環拋出緩存。
“小即是美”的建議比以往更有用,尤其是考慮到核心數據結構必須留在最快的緩存里。
三種常規的策略來減少時延,(a)對可以共享啟動開銷的事務進行批處理,(b)允許事務重疊,和(c)緩存。
一致性在簡單情況下可以保障,所以我絕對不要用二進制緩存,用map做緩存就行了
13 復雜度:盡可能簡單,但別簡單過了頭
13.1 談談復雜度
Unix程序員追求簡單的激情,源自注重實效的事實:復雜度就是成本
更多行的代碼意味著更多的 bug,而調試常常是開發中最昂貴、最耗時的部分。
13.1.2 接口復雜度和實現復雜度的折中
Unix思想中的一個主題就是強調工具小巧銳利,設計從零開始,接口簡單一致。
如果目標是抑制整體復雜度,最愿意犧牲的是什么地方?什么地方又最該被犧牲掉?
本章大多數的問題,良好品味和工程判斷力要求,情況不同,則答案不同。
重要的是要培養斟酌每一個設計的習慣。
正如我們在討論軟件模塊性之前的建議一樣,復雜度的算盤必須打好。
偶然復雜度的產生是因為沒有找到實現規定功能集合的最簡方法。偶然復雜度可以由良好的設計或重新設計來去除。另一方面,選擇復雜度,同某個期望的功能相關聯,只能由改變工程的目標來去除。
計算資源以及人類的思考,同財富一樣,不是靠儲藏而是靠消費來證明其價值的。
13.3.2 折中無用
要么簡約主義,要么無所不能
程序要么小巧,要么龐大,中間道路行不通
所有真正有用的程序,都想變成瑞士軍刀
只有實證了其他方法行不通時,才寫龐大程序
行程才是目的;頓悟在每日的實踐中
第15章
從而讓人心無旁騖地專注于開發中最重要(也是最享受)的部分——設計。 所以設計,是讓人享受的對嗎?
攀爬學習曲線的一次性付出,得到的是更有效編寫程序的能力;
精力也可以更多地放在設計層面而不是低層次的細節操作。
make用于文檔轉換,比如將html標簽文件轉換成易讀的文本格式
看到1287
15.6 運行期調試
困難的是,語法正確的程序并不如期望的那樣運行,解決辦法是
透明設計性:內部數據的流向容易被人眼和簡單工具審視
透明設計有利于防止bug,以及減輕運行期調試任務
牢記Unix哲學,將時間花費在設計質量上,而不是低層次的細節上,盡可能地自動化一切---包括運行期調試的細節工作
make比較出名的工具是autoconf
profiler用來工具,用來檢測性能
15章看完
第16章
不言之教,無為之益,天下希及之。
不愿做不必要的工作是程序員的一大美德
避免重新發明輪子的最有效方法是借用別人的設計和實現。換句話說,重用代碼
Unix的經驗是,養成良好的習慣,嘗試通過最少的新發明,組合現有組件以形成原型,而非匆忙地編寫獨立的、只能使用一次的代碼。
16.2?透明性是重用的關鍵
實際上,任何具有非平凡API的軟件,如果無法深入肌理,甚至無法正確使用。
所以,開放源碼”具有更深遠的意義
設計最好的實踐需要情感的投入,而不是冷漠無聊的過程。軟件開發者,同其它任何類型的工匠和技師一樣;他們想要成為藝術家,這并不是什么私密。
16.4?
在開源世界的開發者,從來不會受最終期限的壓迫,不會一閉眼、一拍腦門就發布軟件。
Unix下工作,最管用的技能之一就是熟練地掌握將代碼粘合在一起的各種方法,從而能夠應用組合原則。
作為Unix開發者,最有價值的時間投資方法之一就是,花時間在這些站點上去了解可以獲得什么東西來重用。節省下來的編碼時間就是自己的 有機會去看一看 1382頁面
更一般地,閱讀代碼是為未來而投資。可以從中學到甚多——新技術、分解問題的新方法、不同的風格和手段。使用代碼和學習代碼都能得到有價值的回報。
寫之前先讀;培養閱讀代碼的習慣。
第17章
好處:無需重寫工具
Unix標準,POSIX,主要是描述:系統調用,C函數庫,shell語義等
標準化,其實就是為了可移植
標準定義的IETF哲學:“我們反對國王、總統和投票。我們信任大致的共識和可運行的代碼”
請求意見稿(英語:Request?for?Comments,縮寫:RFC),又翻譯作意見征求,意見請求,請求評論[1]是由互聯網工程任務組(IETF)發布的一系列備忘錄。
我的理解,RFC也算是一種被提出來的規范,但是流行程度取決于廣大開發者
17.4?
補救糟糕的代碼或設計,比起重新開始嘗嘗更費時費事。
Unix文化主張干脆拆毀重來。
先原型然后循環不斷地測試和演進才是更好的方法。
良好的規格說明(我的理解是程序的說明文檔)具有巨大的價值。
Unix開發中,文檔常常在程序之前,或者至少同程序一起編寫
規格說明,就是一切? 所以規格說明是?需求文檔?
標準,是DNA,我們根據DNA生成RNA,RNA即是根據標準生成的系統,比如Linux
17.5 可移植性編程
可移植性,空間上,可移植到不同主機;時間上,幾十年后仍然能用;時間上的可移植性甚至更重要
通常可以用autoconf來探查本地配置,從而解決可移植性問題
分離信息庫和代碼,我覺得和上面那個手工寫的ASCII常量表有點類似
基于開放源碼編程,而不是專用代碼,這樣能更好應對,接口迭代導致的不兼容
18 文檔:向網絡世界闡述代碼
所見即所得(WYSIWYG)和 已標記為中心的工具(比如xml,html,和latex?) ,標記工具更容易設計格式,布局
unix手冊,一般都有個BUGS部分,事無巨細地揭露軟件的已知缺陷
文檔類型定義(DTD,Document Type Definition)是一種特殊文檔,和XML語法規則相關
看完
19章?
軟件和性一樣,越自由越好
數量多不會被認為是質量高。尤其是,決不要因為害怕別人看不懂而省略功能細節,決不要為了面子而不對存在的問題提出警示。不愿坦露問題才會損害信譽和用戶,坦白了的問題則不會。
開源開發利用了這樣的事實,甄別和修改 bug 的任務適合分解成多個并行的子任務——這和實現某個特殊算法不一樣
盡早發布,經常發布
因此,精工細作,等一切完美了才發布的想法是要不得的。
努力選擇唯一且容易鍵入的名稱前綴
使用GNU自動工具
先測試再發布代碼
發布前對代碼進行健全檢查
“健全檢查(sanity check)”的意思是:使用可以獲得的每一款工具來檢查每個人類易犯的錯誤。使用工具捕捉到的錯誤越多,用戶和自己需要對付的就越少。
比如,使用查找內存泄漏和運行期錯誤的軟件;Electric Fence和Valgrind
擁有FAQ文件可以讓你省很多力氣。當關于項目的某個問題經常出現時,就加到FAQ文件中
發展良好的FAQ可以減少項目維護者一個數量級的負擔,甚至更多。
看完了
20 未來:危機與機遇(看完)
預測未來的最好方法就是去創造未來?
Unix 程序員在 30年風雨中學到最有經驗的回應,就是回到最初的準則——優先從流、命名空間、進程等Unix基本抽象中得到更多效用,而不是增加新的東西。
更優秀解決方案的最危險敵人,就是一個現存的、足夠優秀的代碼庫。
C語言缺乏拋出附帶數據的命名異常的機制。因此,Unix API中的C函數用與眾不同的的返回值(通常是-1或NULL)并設置全局變量errno來報告錯誤。[7]
交互設計也包含了大量堅實的真理,需要為每個Unix程序員所知道。
ioctl和fnctl比較尷尬
通過fcntl設置的都是當前進程如何訪問設備或文件的訪問控制屬性,例如讀、寫、追加、非阻塞、加鎖等,但并不設置文件或設備本身的屬性,例如文件的讀寫權限、串口波特率等。
ioctl函數用于設置某些設備本身的屬性,例如串口波特率、終端窗口大小,注意區分這兩個函數的作用。
波特率即指一個單位時間內傳輸符號的個數。
無名師的Unix心轉
現在得到的90%,比等不來的100%更有價值,他強調實現的健壯性和簡單性
Unix傳統是簡單和空
讀者評論
一個人所擁有的的理念和素質,遠比他所表現出來的專業技能重要的多?????? 我不贊成,有一說一
總結
以上是生活随笔為你收集整理的UNIX编程艺术笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ListView优化问题
- 下一篇: VB 程序设计参考