1. 程式人生 > >解決Linux動態庫版本相容問題

解決Linux動態庫版本相容問題

說道“動態庫版本相容”,很多人頭腦中首先蹦出的就是“Dll Hell”。啊,這曾經讓人頭疼的難題。時至今日,這個難題已經很好地解決了。
 
在進一步討論之前來思考一個問題:Linux下為什麼沒有讓人頭痛的“DllHell”?
回答這個問題,非常easy,因為——Linux下根本沒有dll!
 
哈哈,當然這只是個玩笑,接下來展開一下這個話題,很多有動態庫的系統都會面臨這個難題,但各自解決的思路卻各不相同。
 
Dll hell是指windows 上動態庫新版本覆蓋舊版本,但是卻不相容老版本。常常發生在程序升級之後,動態庫更新,原有程式執行不起來;或者裝新軟體,但是已有的軟體執行不起來。
 
一、linux下的解決方案——命名規範
Linux 上的Dll ,叫sharedlibrary。Linux 系統面臨和Window一樣的問題,如何控制動態庫的多個版本問題。為解決這個問題,Linux 為解決這個問題,引入了一套命名機制,如果遵守這個機制來做,就可以避免這個問題。但是這隻事一個約定,不是強制的。但是建議遵守這個約定,否則同樣也會出現 Linux 版的Dll hell 問題。
 
Real Name
首先是共享庫本身的檔名:共享庫的命名必須如 libname.so.x.y.z
最前面使用字首”lib”,中間是庫的名字和字尾”.so”,最後三個數字是版本號。x是主版本號(Major Version Number),y是次版本號(Minor Version Number),z是釋出版本號(Release Version Number)。
 
主版本號(不相容):重大升級,不同主版本的庫之間的庫是不相容的。所以如果要保證向後相容就不能刪除舊的動態庫的版本。
 
次版本號(向下相容): 增量升級,增加一些新的介面但保留原有介面。高次版本號的庫向後相容低次版本號的庫。
 
釋出版本號(相互相容):庫的一些諸如錯誤修改、效能改進等,不新增新介面,也不更改介面。主版本號和此版本號相同的前提下,不同釋出版本之間完全相容。
 
SO-NAME
嚴格遵守上述規定,確實能避免動態庫因為版本衝突的問題,但是讀者可能有疑問:在程式載入或執行的時候,動態連結器是如何知道程式依賴哪些庫,如何選擇庫的不同版本?
Solaris和Linux等採用SO-NAME( Shortfor shared object name )的命名機制來記錄共享庫的依賴關係。每個共享庫都有一個對應的“SO-NAME”(共享庫檔名去掉次版本號和釋出版本號)。比如一個共享庫名為libtest.so.3.8.2,那麼它的SO-NAME就是libtest.so.3。
 
 
在Linux系統中,系統會為每個共享庫所在的目錄建立一個跟SO-NAME相同的並且指向它的軟連線(Symbol Link)。這個軟連線會指向目錄中主版本號相同、次版本號和釋出版本號最新的共享庫。也就是說,比如目錄中有兩個共享庫版本分別為:/lib/libtest.so.3.8.2和/lib/libtest.so.3.7.5,麼軟連線/lib/libtest.so.3指向/lib/libtest.so.3.8.2。
 
建立以SO-NAME為名字的軟連線的目的是,使得所有依賴某個共享庫的模組,在編譯、連結和執行時,都使用共享庫的SO-NAME,而不需要使用詳細版本號。在編譯生產ELF檔案時候,如果檔案A依賴於檔案B,那麼A的連結檔案中的”.dynamic”段中會有DT_NEED型別的欄位,欄位的值就是B的SO-NAME。這樣當動態連結器進行共享庫依賴檔案查詢時,就會依據系統中各種共享庫目錄中的SO-NAME軟連線自動定向到最新相容版本的共享庫。
 
★  readelf -d sharelibrary 可以檢視so-name
★  Linux提供了一個工具——ldconfig,當系統中安裝或更新一個共享庫時,需要執行這個工具,它會遍歷預設所有共享庫目錄,比如/lib, /usr/lib等,然後更新所有的軟連結,使她們指向最新共享庫。
 
Link Name
 
當我們在編譯器裡使用共享庫的時候,如用GCC的“-l”引數連結共享庫libtXXX.so.3.8.1,只需要在編譯器命令列指定 -l XXX 即可,省略了字首和版本資訊。編譯器會根據當前環境,在系統中的相關路徑(往往由-L引數指定)查詢最新版本的XXX庫。這個XXX就是共享庫的“連結名”。不同型別的庫可能有相同的連結名,比如C語言執行庫有靜態版本(libc.a)也動態版本(libc.so.x.y.z)的區別,如果在連結時使用引數”-lc”,那麼聯結器就會根據輸出檔案的情況(動態/靜態)來選擇合適版本的庫。eg. ld使用“-static”引數時嗎,”-lc”會查詢libc.a;如果使用“-Bdynamic”(預設),會查詢最新版本的libc.so.x.y.z。
 
更詳細可以參見
http://www.linuxidc.com/Linux/2012-04/59071.htm
 
 
.Net下的解決方案——Manifest檔案
.Net框架中,一個程式集(Assembly)有兩種型別:應用程式程式集(.exe)與庫程式集(DLL動態連結庫)。一個程式集包括一個或個檔案,所以需要一個清單檔案(Manifest檔案)來描述程式集。Manifest檔案描述了程式集的名字,版本號以及程式集的各種資源,同時也描述了該程式集的執行所依賴的資源,包括DLL以及其他資原始檔等。Manifest是一個XML檔案。每個DLL有自己的Manifest。對於應用程式而言,manifest檔案可以和可執行檔案在同一目錄,也可以作為一個資源嵌入到可執行檔案內部(Embed Manifest)。
 
XP以前的windows版本,在執行可執行檔案是不會考慮manifest檔案的。它會直接到system32目錄下查詢可執行檔案鎖依賴的DLL。在這種情況下,manifest是多餘的。XP之後的作業系統,在執行可執行檔案時則會首先讀取程式集的manifest檔案,獲得該可執行檔案需要呼叫的DLL列表,作業系統再根據DLL的manifest檔案去尋找對應的DLL呼叫。
 
 
Windows的解決方案——COM元件
採用標準COM元件,有很多好處:面向介面和物件程式語言無關性,採用二進位制標準,可以實現跨語言呼叫版本升級方便,增加新介面,元件升級後老客戶程式不用重新編譯位置透明,客戶程式不用關心元件的位置重用方便,通過包容和聚合可以快速重用已有元件我們可以看到標準COM元件非常強大,但是很多時候我們並不需要標準COM元件的所有特性,比如我們不希望引入登錄檔,也不希望引入COM執行庫,我們希望我們的程式是完全“綠色”的。這時我們就會採用“COM思想架構“開發非標準的COM元件。


轉自 http://blog.sina.com.cn/s/blog_5cf54f0e0101cpct.html