筆誤,是福是禍?(linux so搜尋路徑,SWIG等)

來源:互聯網
上載者:User

        這是一篇非常有聊(?)的日記。

        昨晚心血來潮,在嘗試用SWIG(http://www.swig.org)給C++的庫做Python綁定,網上例子非常多,看了幾個就動手。後來被動態連結的問題折騰到晚上十二點半,對我這個每天早上7點前起床的人來說有點傷不起。而且,你知道嗎,錯誤原因是我把函數名寫錯了,測試用源碼裡的randnum在介面裡我寫成了rand_num。

        SWIG的工作原理是:你提供原始碼,然後把源碼裡的部分聲明抽出來,寫成一個 副檔名.i 的檔案提供給SWIG,再指定你的源碼語言(比如C++)和目標語言(比如Python),SWIG會幫你產生一個長長的膠水源碼,這個膠水代碼就是python和C++的橋樑了。

        編譯的時候比較複雜,我直接說目的以便理清思路:最終你會得到兩個動態連結程式庫,一個是膠水庫,一個是實際幹活的庫。膠水庫是用膠水代碼產生的,可以被python識別,而且裡面沒有具體函數實現。實際幹活的庫就是你的源碼編譯成的so檔案,它和python、SWIG一點關係都沒有是純正的C/C++庫,它會被膠水庫調用。此外為了做一些管理工作還會產生一個xxxx.py 源碼,寫python代碼的時候直接import xxxx就可以了。

        既然有兩個so檔案,這就涉及到so搜尋的問題。敢寫這篇日記說明我已經基本搞清了:)。

        首先,python有自己的匯入dll/so擴充庫的機制,可以通過python標準庫的imp模組的imp.load_module實現,參數裡可以指定路徑。膠水庫通過這種方式匯入,這個很好理解。

        其次,在匯入膠水dll/so的時候,會匯入相應依賴的dll/so,這個匯入是作業系統層級的,也就是是C/C++領域的問題,和python沒有關係。這個匯入能否成功取決於兩點:在搜尋路徑下能搜尋到相應檔案;這個檔案裡能找到所有需要的符號定義(函數實現),如果有未定義的符號會報錯(至少運行時肯定會報錯,這個地方表述不是完全準確,建議通讀《程式員的自我修養——連結、裝載與庫》的前半部分。我不想誤人子弟。)

        既然這個匯入取決於兩個關鍵點,那麼這兩個關鍵點都有可能出錯。

        1、搜尋路徑問題。windows和linux的動態連結程式庫,除了命名方式不同,還有個很大區別是搜尋方式不同。windows預設先搜尋目前的目錄,linux預設不搜尋目前的目錄。具體順序挺麻煩,請百度。在windows下只要把dll拷貝到當前工作目錄就能用了(windows有個麻煩的事情,是注意兩個so的命名不要一樣了,可能造成衝突)。

            linux建議這麼做:執行shell命令export LD_LIBRARY_PATH=“工作目錄”。這樣可以臨時的把目前的目錄加進來,而且不影響其他進程。如果你的庫已經穩定了,就把你的so拷貝到/usr/local/lib或者類似的地方,它們是預設的so搜尋路徑。

        2、動態連結程式庫裡的定義是否完整。為啥會不完整捏?舉我犯過的這個腦殘的例子。在膠水so裡,我聲明了一個函數rand_seed,在實際so裡,沒有rand_seed函數,只有randseed函數,而且這個錯誤在複雜的編譯過程中都不會被發現,只有在動態載入這個工作so時才會發現這個錯誤!由於載入工作so的時候你已經在python環境裡了,錯誤提示會讓你很納悶。

        一開始我猜測還有一種找不到函數定義的情況:C++對函數名做了修飾(在非入門書籍上應該都有提到),但是我後來發現python會去找 _Zrand_numV_類似這種樣子的符號,也就是說swig把這些問題都處理妥當了,不用擔心。

        做這種麻煩的事情,關鍵是理清思路,如果照著教程順利走下來,真是沒有什麼收穫,而且遇到複雜錯誤會茫然不知所措。基礎知識是王道:)

附很好的一篇SWIG,C++ Python 入門教程:

http://hi.baidu.com/125725385/blog/item/8811a511d3cbae07213f2e97.html

———————————————一覺醒來,對上一節一些錯誤的修正—————————————————————

        仔細看了一天SWIG,納悶為啥要有兩個動態連結程式庫,放在一起豈不是更方便。仔細讀了官方文檔,果不其然。

        http://www.swig.org/Doc1.3/SWIGDocumentation.html#Python

        其中的:31.2.2  Using distutils,其方法就是寫setup.py的時候,把倆庫連結到一起,這樣使用起來就沒有上面那種方法找不到so的麻煩了。恩。但是按照本日記的核心思想,只有給自己找麻煩才能進步。

        又學到一招神技:如果編譯一個liba.so時引用到另一個libb.so,那麼可以把b靜態連結到a裡,變成一個so,發布給別人的時候可能方便一些。大致寫法如下,主要是用到gcc的 -Xlinker 參數。       

$ gcc -shared example.o example_wrap.o -L/home/beazley/projects/lib -lfoo \      -Xlinker -rpath /home/beazley/projects/lib  \      -o _example.so

———————————————————————————————————————————————————

        幾個月前,一位同事給我們的遊戲項目GUI部分做控制項擴充。我們的GUI介面都是用xml檔案配置的,支援xml包含,類似C語言的include。他忙活了一天發現了一個詭異的xml包含時錯誤,百試不得其解。他給我看了這個問題,我看過配置也很頭疼。後來我倆分工,我調試源碼,他查配置,倆人折騰了一個多小時。最後就在我快要搞清xml包含機制的時候,他發現了問題:單詞拼錯了。

        我感謝他的這次拼字錯誤,如果沒有這次拼字錯誤,xml讀取機制在我腦海中一直是個“謎團”(雖然現在仍然是小謎團)。順帶說一句,項目整個的GUI庫包括獨家xml庫都是一位大俠自己寫的。

        思緒飄到了去年夏天,一位大俠擴充了指令碼引擎代碼,然後發生詭異的編譯時間異常。我查了一周多的時間,發現是擴充指令集的時候,最佳化用數組忘記擴充了,導致越界。我想說的是,在這個過程中,我用到了記憶體流失檢查工具(原理是重載new進行記錄),瞭解了各種VC++配置,知道了5種神奇的Debug技巧,生平第一次靠彙編代碼偵錯工具。最後,這個可愛的BUG引領我走進了《編譯原理》的大門……至少是完全瞭解了詞法分析(至今停留在文法分析那章)。

        我想說的是,一個有經驗的程式員肯定有過不少類似的經曆。對一個半吊子程式員來說尚且如此,更不用談各種高手的成長經曆了。其實對任何腦力勞動者,比如數學家、科學家可能都免不了這些經曆。

        總結一下,讓自己記住:遇到任何小問題,不要輕易放過,更不要一開始就嘗試繞過去。正面和它死磕,有極大可能性你會揪出一隻大象。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.