1. 程式人生 > >Linux下靜態庫、動態庫的建立和使用

Linux下靜態庫、動態庫的建立和使用

Linux下靜態庫、動態庫的建立和使用

Linux庫檔名由:字首lib、庫名和字尾3部分組成,靜態庫通常以.a作為字尾,動態庫以.so作為字尾,

Linux下把動態庫叫做共享庫,so即shared object的縮寫。

靜態庫是程式編譯連結時使用,動態庫是程式執行時使用


預備知識:

a)  2個重要的環境變數:

LIBRARY_PATH : 庫檔案(靜態庫或者動態庫)搜尋路徑,編譯器連結時,需要去這些路徑搜尋相應的庫,當然系統預設的庫檔案搜尋路徑是/usr/lib和/lib。 

LD_LIBRARY_PATH :動態庫檔案搜尋路徑,程式執行時,需要去這些路徑搜尋相應的動態庫,程式執行時,預設
的搜尋動態庫的路徑也是/usr/lib和/lib。



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
,因此當/usr/lib或者/lib下放入新的動態庫後,也要執行sudo ldconfig 重新生成快取檔案,否則程式無法執行,報錯提示,找不到共享庫。當往/etc/ld.so.conf裡新增目錄時,或者後來在這些目錄下新增新的庫,都需要執行sudo ldconfig生成新的快取檔案。

==================================================================================


假設有如下檔案:mylib.h mylib.c  depend.h depend.c 


一、建立靜態庫

   

  gcc -c mylib.c -o mylib.o

        gcc -c depend.c -o depend.o

        ar rcs libmylib.a  mylib.o depend.o


       ar 其實就是將*.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 -lmylib
 
       3、將/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下的動態庫》。



以上內容是筆者在工作中,使用動態庫、靜態庫的個人總結,今作記錄於此,由於筆者的水平有限,出錯在所難免,懇請讀者拍磚指正,謝謝閱讀。