2018-08 更新
依近幾年的經驗更新了另一份介紹,見這裡。
新加入一個專案,最先面對的課題是如何正確地編譯和執行專案,可從 "It works on my machine" 如此地風行,印證這件事的困難性;再來則是閱讀負責工作相關的程式碼。至於發揮程式語言的特性,運用高階設計模式等,都是另開新專案或熟悉狀況後才有機會發揮。
過去數年沉浸在愉快的 scripting language 和開發新專案中,一直沒踏入這殘酷的世界。這篇記錄在這樣的情境下,可能需要的技能,結算一下這一個多月的心得,全都是血淚談啊 ...。
系統工具
熟悉作業系統的安裝套件是首要之務,這樣才知道如何補足需要的 header、library,或是安裝含 debug symbol 版的函式庫以執行 gdb 觀察程式或除錯。參見《自行編譯含 debug symbol 的套件 (package)》了解 Ubuntu/Debian 下的套件命名規則。
在未安裝套件的情況下,可用
- aptitude search SUBSTRING # 找套件
- aptitude show PACKAGE # 顯示套件用途
- apt-file search X # 找出 X 包在那個套件裡,找 header 時很有用。
注意在用 apt-file 前要先跑 sudo apt-file update,不然搜不出東西來。
對於已安裝套件,可用
- dpkg --search SUBSTRING # 找出安裝在那個套件,已知 header 時,適合用來找 library
- dpkg -L PACKAGE # 列出套件內容,可用來找 header、library
- locate SUBSTRING # 我比較常用它找 header 的位置,再觀看 header 內容
執行 locate 前記得先執行 sudo updatedb,原因同 apt-file。
《除錯技巧:在 Ubuntu 上找出第三方函式庫的程式碼》用一個小例子說明如何使用這些工具找出原始碼協助除錯。
編譯
- 參考《解決 undefined symbol / reference》了解整個編譯的流程,先有觀念才清楚問題的環節,才能選對工具檢查問題。
- 另在《從 C 呼叫 C++ 函式的過程理解程式編譯、連結的原理》以一個小個案,從另一個角度描述編譯的流程來除錯。
- 《debug info 和 optimization》提到 -O 和 -g 可同時用,以及注意事項。
- 《讀懂函式庫的 man page》 說明使用系統函式庫時,如何從 man page 得知該定義的 feature test macro 和連結用的參數。
連結
這一塊讓我卡了一陣子。一些粗淺心得:
- 《列出用到的 shared library》。
- 《加速 linking time》
- 《ld, ld.so 和 ldconfig 的行為》
- 《(C/C++ ) 如何在 Linux 上使用自行編譯的第三方函式庫 》
執行
光只是讀程式碼就像大海撈針一樣,不太有效率。可從動態執行過程找出主要執行的路徑,再專注相關的程式碼。
1. strace 和 ltrace
srace 是分析執行行為的強大工具,google 一下會看到很多別人的個案心得,看看再自己試一試,很快能上手,不知能發揮它多少功能。這裡列自己用的兩個小案例:
反而是 ltrace 一直都想不到使用它的時機,也沒找到好的個案心得文。
2. gdb
gdb 的重要性不需多說明,之前的幾則心得:
強烈建議使用 cgdb,簡易安裝 + 無痛上手,瞬間省下大量操作和讀碼的時間。
3. 打開除錯功能
依照開發者的習性,一定會留後門讓自己方便除錯,從這角度下手也可省下不少時間:
4. 載入函式庫
- 若在編譯、連結時無法解決相依問題,可考慮偷吃步在載入程式時用 LD_PRELOAD 換掉部份函式。
- 另外備忘用 LD_LIBRARY_PATH 補充載入 shared library 的位置,目前仍沒用過它。
除以上所言外,我另外有找過畫出程式流程的靜態和動態分析工具,像是畫 call graph 或是 C 的 cflow。不過 C++ 的靜態分析效果很糟,就沒花太多時間研究。目前用 strace 和 gdb 覺得已夠用了,不知用工具產生 call graph、class 相依圖或其它東西,是否會更有幫助。待有需求看整體的程式時再來試試。
閱讀程式碼
聽了大家的建議後,做了一些實際操作,而有些心得:
Eclipse CDT 雖然方便,後來我還是用 gj 居多。原因有幾點:
另外 ack 也滿方便的,懶得建 index 或是想比對子字串時,可直接使用。當然 id-utils 也支援子字串比對,只是暫時懶得為此修改 gj 的程式,目前大部份需求是找完整的 symbol。
熟悉 Linux 系統程式
在基本工具都上手後,打算每天抽一點時間加減讀一點相關知識。一兩年下來應該會有不錯的成果。目前打算讀《The Linux Programming Interface》,年假時試看看效果如何。
這一個月的心得以了解 /proc 為主,對觀察 CPU 用量、RAM 用量、載入那些函式庫、multi-thread、程式執行狀態等都很有幫助:
結論
即使大概知道有那些東西,還是需要實際動手的經驗,才會真的學進去。一個月下來進步了不少,不過對於要面對的戰役,還有一大段路要趕上,還有很多很多要學的。
2012-01-29 更新
補上一些後來新寫的連結。此外,《The Linux Programming Interface》 相當實用,讀 ch1 ~ 3 讓我補足不少基礎知識。ch41、42 講解 shared library 也相當值得一看。相關心得見《The Linux Programming Interface 讀書心得》。
2013-07-13 更新
備忘效能分析相關的工具:
出處:Linux Performance Analysis and Tools
2013-07-20 更新
將後半部份內容抽出來,另寫了一篇比較完整的文章:《了解 C/C++ 程式行為的技巧》。
我想請問一下
回覆刪除為什麼要開四個部落格來做書寫紀錄的事情?
開一個部落格,做四種分類,不好嗎?
並非質問的意思,只是這種感覺很怪。
要看技術要訂閱技術部落格,要看生活要另外訂閱生活部落格,
要看學術還要再訂閱學術部落格。
而這篇文章,引用的資料還是技術部落格,
所以四個部落格的資料是互相引用的?
那這樣的分類部落格意義是?
看了您很多分享,也學了不少,我想憑您的技術,
只開一個部落格,做四種分類,這種事應該不難。
沒什麼意思,只是很納悶。
四個還不算多, 望向 gslin 的 blog ..., 開開玩笑而已
回覆刪除很久以前還沒寫 blog 時, 我是全混著寫在 bbs 個版, 結果發現固定一群朋友會看 ACG, 另一群朋友會看技術等, 所以一直想著是否要分類。
一開始只有這個 blog, 但寫了一段時間後, 發現有一部份非資工圈的朋友, 完全看不懂技術的東西, 也不熟網路, 要丟一個我的生活相關的事給他們看, 光用分類有些困難 (生活相關仍有可能分成「閒書」、「音樂」、「日常生活」、「人生探索」等)。所以切了另一個寫生活。
後來發覺有很多技術上瑣碎的小事情, 寫在 bbs 上不易保存也不方便搜尋, 但是寫在這個 blog 又格格不入, 那些大多是幾行 linux 指令的短文。於是切了另一個「技術隨手記」的 blog, 想到什麼就寫, 而這裡照舊放長時間累積消化後的心得。換句話說, 我對這兩個技術 blog 的定位是: 這裡量少而品質高, 另一個是量多, 品質隨意。就這樣寫看看, 順便觀察會不會有什麼新發現。結果到是有滿多有趣的心得, 不過這是另一個話題了。平時兩者不太會有交集, 像這樣大量參照過去, 是頭一回事。
至於學術部落格, 則是考量到學術心得相當難產, 看這個部落格有關 machine learning 之類的文章量即知道, 其實還有很多有趣的心得, 不過沒動力和時間消化它們。所以想效法「技術隨手記」, 來隨手寫些短一點的心得, 對照「技術隨手記」的經驗來看, 有寫總比沒寫好。不過實驗的結果是, 即使是學術短文, 仍需頗長的時間消化吸收, 所以還積了幾篇論文讀了沒時間寫, 就不了了之放在那裡了 ...
個人覺得分開不同的blog寫還蠻聰明的,可以讓自己寫起來比較沒顧慮
回覆刪除謝謝你的文章,完全正中我的需要
回覆刪除感謝你 讓入門的linux C/C++新手很受用:)
回覆刪除Thanks for sharing such an useful and informative post with us.
回覆刪除Linux training in Pune