在近一段時間里,由于多次參與相關(guān)專業(yè)軟件Linux運行環(huán)境建設(shè),深感有必要將這些知識理一理,供往后參考。
編譯時和運行時
縱觀程序編譯整個過程,細(xì)分可分為編譯(Compiling,指的是語言到平臺相關(guān)目標(biāo)文件這一層次)和鏈接(Linking,指目標(biāo)文件到最終形成可執(zhí)行文件這一層次),這個總的過程可稱為編譯時;就動態(tài)鏈接而言,還存在一個運行時,即程序在被操作系統(tǒng)加載的過程中,系統(tǒng)將該程序需要的動態(tài)庫加載至內(nèi)存到程序開始運行的這一段過程。明確這兩個過程在一般linux開發(fā)中的地位,以及了解每個“時”所遇要的文件、所運行的工具對理解linux動態(tài)鏈接很重要。
Linux共享庫命名規(guī)范
回顧linux對動態(tài)庫的命名規(guī)則,在一般情況下,均命名為libname.so.x.y.z。各版本號意義與兼容性標(biāo)準(zhǔn)如下表所示,這些也是程序員進行庫程序設(shè)計是應(yīng)該盡量遵守的設(shè)計規(guī)范。
版本號 | 意義 |
x | 主版本號,表示重大升級,不同主版本的庫之間是不兼容的 |
y | 次版本號,表示增量升級,高次版本向后兼容低次版本號的庫 |
z | 發(fā)布版本號,表示一些錯誤修正,性能改進,不同發(fā)布版本號之間完全兼容 |
這樣的設(shè)計很容易帶來一個問題,即動態(tài)庫如何進行有效地升級,而不要求依賴它的應(yīng)用程序重新編譯?比如demo.out依賴libsmath.so.1.0.0(該名稱在編譯已指定,儲存在ELF文件的DT_NEEDED段中),現(xiàn)對該庫升級至1.1.0,但demo.out程序已經(jīng)無法使用新版本的庫。為解決這樣的問題,Linux引入一種新的機制SO-NANE.
SO-NAME版本控制
由上可知,共享庫的主版本和次版本決定該共享庫的接口,則對于依賴某庫的應(yīng)用程序,最小限度上只需記錄該庫的主版本號,具體的版本即次版本和發(fā)布版本,可以交給動態(tài)鏈接器(一般為ld-linux.so)來選擇,這樣便可以在一定程度上解決上述的問題。
具體設(shè)計實現(xiàn)方面,對于libname.so.x.y.z,省略其y和z,只記錄成libname.so.x即為該共享庫的SO-NAME,在需要該庫的應(yīng)用程序的DT_NEEDED段中記錄這個SO-NAME,同時在動態(tài)鏈接器的庫搜索目錄添加這個庫的軟鏈接,指定為libname.so.x(可通過設(shè)置/etc/ld.so.conf或設(shè)置環(huán)境變量LD_LIBRARY_PATH)。這樣對于libname.so.x.y.z的次版本和發(fā)布版本的升級,只需更新該軟鏈接就行,應(yīng)用程序無須重新編譯。
同時,SO-NAME也會被記錄在該動態(tài)庫內(nèi),供ldconfig工具自動更新軟鏈接所用,這也就是每次安裝新軟件包后一般會運行該工具的原因。
以上這些均是對于運行時而言的,在編譯時同樣需要一些該庫的信息,指定所需要的庫,即gcc的-lname選項。該選項主要是傳遞給鏈接器(ld,而非運行時所用的ld-linux.so),鏈接器會在庫搜索目錄(可通過設(shè)置-rpath參數(shù)或LIBRARY_PATH環(huán)境變量)搜索libname.so文件,所以為了能使用該庫進行開發(fā),需要在以上目錄設(shè)定指向libname.so.x或libname.so.x.y.z的軟鏈接,名稱為libname.so.這項工作一般需手動完成。
示例
(下述例子中,smath.h、libsmath.so為編譯時需要文件;libsmath.so.1、libsmath.so.1.0.0為運行時所需文件)
假設(shè)需要自定義編寫一套數(shù)學(xué)庫,其中有Add和Sub等函數(shù)
1 //smath.h2 3 int Add(int,int);4 5 int Sub(int,int);
1 //smath.c 2 3 int Add(int a,int b) 4 5 { 6 7 return a+b; 8 9 }10 11 int Sub(int a,int b)12 13 {14 15 return a-b;16 17 }①編譯
按照gcc操作手冊
執(zhí)行
gcc -shared -fPIC -Wl, -soname,libsmath.so.1 -o libmath.so.1.0.0 smath.c
(若不指定-soname,則該庫沒有SO-NAME,DT_NEEDED中記錄為空)
②安裝
復(fù)制頭文件
cp smath.h ~/include
安裝至自定義目錄(非/lib、/usr/lib等),假設(shè)安裝至~/lib下
cp libsmath.so.1.0.0 ~/libcd ~/libln -s libsmath.so.1.0.0 libsmath.so.1ln -s libsmath.so.1 libsmath.so
③使用
保證smath.h 、libsmath.so在相關(guān)搜索目錄下(編譯鏈接用)
export C_INCLUDE_PATH=$HOME/includeexport LIBRARY_PATH=$HOME/include
(也可設(shè)置gcc相關(guān)參數(shù)設(shè)置)
則現(xiàn)在可使用gcc編譯如下代碼
main.c#include <stdio.h>#include <smath.h>int main(){ int a=3,b=5; PRintf(“%d + %d = %d/n”,a,b,Add(a,b)); return 0;}編譯
gcc -omain.out -lsmath main.c
④運行
保證libsmath.so.1在相關(guān)搜索目錄下(運行鏈接用)
export LD_LIBRARY_PATH=$HOME/lib
(也可設(shè)置/etc/ld.so.conf目錄)
即可運行
參考
《程序員的自我修養(yǎng)——鏈接、裝載與庫》 第8章
Working with libraries and the linker -- http://bottomupcs.sourceforge.net/csbu/x4012.htm
新聞熱點
疑難解答
圖片精選