Linux下靜態庫、動態庫的建立和使用
Linux庫檔名由:字首lib、庫名和字尾3部分組成,靜態庫通常以.a作為字尾,動態庫以.so作為字尾,
Linux下把動態庫叫做共享庫,so即shared object的縮寫。
靜態庫是程式編譯連結時使用,動態庫是程式執行時使用。
預備知識:
a) 2個重要的環境變數:LIBRARY_PATH : 庫檔案(靜態庫或者動態庫)搜尋路徑,編譯器連結時,需要去這些路徑搜尋相應的庫,當然系統預設的庫檔案搜尋路徑是/usr/lib和/lib。
LD_LIBRARY_PATH :動態庫檔案搜尋路徑,程式執行時,需要去這些路徑搜尋相應的動態庫,程式執行時,預設b) 2個重要的系統檔案:
/etc/ld.so.conf :裡面可以加上動態庫的搜尋路徑,絕對路徑,(/usr/lib和/lib預設包含其中)
/etc/ld.so.cache :儲存/etc/ld.so.conf裡所有路徑的所有的動態庫檔案的快取,儲存了已排好序的動態連結庫名字列表,有一個命令:ldconfig 會根據/etc/ld.so.conf生成或者更新/etc/ld.so.cache,執行這個命令需要root許可權。(關於這個cache檔案裡面具體實現哪些東西,筆者也不是很清楚,待後續研究)
程式在執行時,如果都要搜尋/etc/ld.so.conf裡目錄下庫檔案,效率會很低,因此Linux採用了類似快取記憶體制,程式先去查詢快取檔案/etc/ld.so.cache
==================================================================================
假設有如下檔案:mylib.h mylib.c depend.h depend.c
一、建立靜態庫
gcc -c depend.c -o depend.o
ar rcs libmylib.a mylib.o depend.oar 其實就是將*.o檔案打包歸檔而已,主要引數說明:
r --- 在庫中加入成員檔案,若存在,則替換
c --- 建立一個靜態庫
s --- 無論ar命令是否修改了庫的內容,都強制重新生成庫符號表
d --- 從庫中刪除成員檔案
二、使用靜態庫(3種方法)
1、把庫檔案放入/usr/lib或者/lib目錄下
編譯: gcc main.c -o main -lmylib
2、連結時指定庫的目錄,假設庫檔案放在/home/user/lib/下
編譯: gcc main.c -o main -L/home/user/lib -lmylib
3、修改環境變數 將庫檔案所在目錄/home/user/lib/加入到LIBRARY_PATH
執行shell: export LIBRARY_PATH=/home/user/lib/:$LIBRARY_PATH 編譯:gcc main.c -o main -lmylib
三、建立動態庫(2種方法)
1、 gcc -fpic -c mylib.c -o mylib.o
gcc -fpic -c depend.c -o depend.o
gcc -shared -o libmylib.so mylib.o depend.o
2、gcc -fpic -shared mylib.c depend.c -o libmylib.so
四、使用動態庫(4種方法)
假設libmylib.so庫檔案在/home/user/lib/下,以下4種方法:
1、把動態庫放入 /usr/lib或者/lib下,然後執行sudo ldconfig
編譯:gcc main.c -o main -lmylib
2、修改環境變數
export LD_LIBRARY_PATH=/home/user/lib/:$LIBRARY_PATH
gcc main.c -o main -lmylib ,注意這種方式,不能編譯通過,因為LD_LIBRARY_PATH只和執行時有關,
所以正確的做法是:
編譯:gcc main.c -o main -L/home/user/lib -lmylib
或者
將/home/user/lib加入到LIBRARY_PATH中,然後可以編譯: gcc main.c -o main -lmylib3、將/home/user/lib/加入到/etc/ld.so.conf中,執行sudo ldconfig
編譯:gcc main.c -o main -lmylib
4、在編譯時,設定動態庫執行時尋找路徑
gcc main.c -o main -Wl,-rpath=/home/user/lib ,注意這樣編譯是通不過的,-Wl,-rpath= 指定程式
在執行時,搜尋動態庫會第一個搜尋這個指定目錄,然後是LD_LIBRARY_PATH,再是/etc/ld.so.conf
中的路徑。
正確的做法:編譯:gcc main.c -o main -L/home/user/lib -lmylib -Wl,-rpath=/home/user/lib
結論 :程式執行時,動態庫搜尋路徑順序:1、編譯時-Wl,rpath=指定的路徑 2、LD_LIBRARY_PATH 3、/etc/ld.so.cache,即/etc/ld.so.conf中的路徑。
根據筆者的實踐經驗,筆者推薦使用第4種方法,原因如下:
1、對於方法1,誠然簡單方便,易於批量部署,但是假如我使用動態庫在目標機器也存在,比如 libcurl.so,boost庫,如果我使用的libcurl.so比目標機器上的libcurl.so庫版本高,那麼就等於升
級了目標機器的libcurl.so庫檔案,雖然Linux約定了動態庫版本相容的規則,但是對於隨意升級
目標機器上的的庫檔案,有時候會造成一些問題,顯然汙染了系統檔案的純潔。
2、對方法2和方法3都需要修改配置檔案,比較麻煩,不易於批量部署,而且也容易汙染系統檔案
的純潔。
3、方法4的庫檔案路徑可以自己定義,與系統庫檔案隔離了,解決了方法1,2,3存在的問題。然而在實踐過程中,使用方法4會出現一些莫名其妙的問題。
比如我的程式依賴這個庫libcurl.so.4.3.0(一個有名的開源庫),並且放在/home/user/lib下,
編譯:gcc main.c -o main -L/home/user/lib -lcurl -Wl,-rpath=/home/user/lib這種編譯根本通不過,為何?顯然 -lcurl 意思是連結到libcurl.so這個檔案,而我的庫檔案是libcurl.4.3.0,當 然連結不到,無法編譯。
應該這樣編譯:gcc main.c -o main /home/user/lib/libcurl.so.4.3.0 -Wl,-rpath=/home/user/lib
可以編譯通過,但是程式無法執行。
為什麼會出現這種問題呢?該如何解決呢?請參見下一篇部落格《再談Linux下的動態庫》。
以上內容是筆者在工作中,使用動態庫、靜態庫的個人總結,今作記錄於此,由於筆者的水平有限,出錯在所難免,懇請讀者拍磚指正,謝謝閱讀。