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

Linux下動態連結庫的建立和使用

1、連結庫的基本知識

    庫是一種軟體元件技術,庫裡面封裝了資料和函式。它的使用,可以是程式模組化。在程式中使用,我們可以稱之為程式函式庫。
    程式函式庫可分為3種類型:靜態函式庫(static libraries)、共享函式庫(shared libraries)、動態函式庫(dynamically loaded libraries):
    1、靜態函式庫,是在程式執行前就加入到目標程式中去了;
    2、共享函式庫,則是在程式啟動的時候載入到程式中,它可以被不同的程式共享
    3、動態函式庫,並非另外一種庫函式格式,它只是使用動態載入方式載入共享函式庫。

    Windows系統包括靜態連結庫(.lib檔案)和動態連結庫(.dll檔案)。
    Linux通常把庫檔案存放在/usr/lib或/lib目錄下
    Linux庫檔名由:字首lib、庫名和字尾3部分組成,其中共享連結庫以.so.X最為字尾, .X是版本號,靜態連結庫通常以.a作為字尾。
    Linux下標準庫連結的三種方式(全靜態 , 半靜態 (libgcc,libstdc++), 全動態。 三種標準庫連結方式的選項及區別見下表。

三種標準庫連結方式的選項及區別:

標準庫連結方式 示例連線選項 優點 缺點
全靜態 -static -pthread -lrt -ldl 不會發生不同Linux 版本下的標準庫不相容問題 生成的檔案比較大,應用程式功能受限(不能呼叫動態庫等)
全動態 -pthread -lrt -ldl 生成的檔案最小 不同Linux版本下標準庫依賴不相容問題
半靜態(libgcc,libstdc++) -static-libgcc -L. -pthread -lrt -ldl 靈活度大,針對不同的標準庫採取不同的連結策略,從而避免不相容問題發生 難識別哪些庫容易發生不相容問題,會因選擇的標準庫版本而喪失某些功能

上述三種標準庫連結方式中,比較特殊的是 半靜態連結方式,主要在於其還需要在連結前增加額外的一個步驟:
ln -s ‘g++ -print-file-name=libstdc++.a’,作用是將 libstdc++.a(libstdc++ 的靜態庫)符號連結到本地工程連結目錄
-print-file-name在gcc中的解釋如下: Display the full path to library

ldd 簡介:該命令用於打印出某個應用程式或者動態庫所依賴的動態庫 ,使用該命令我們可以觀察到Linux標準庫三種連結方式的區別。
從實際應用當中發現,最理想的標準庫連結方式就是半靜態連結,通常會選擇將 libgcc 與 libstdc++ 這兩個標準庫靜態連結,從而避免應用程式在不同 Linux 版本間標準庫依賴不相容的問題發生。

size 簡介:該命令用於顯示出可執行檔案的大小

示例連結選項中所涉及命令(引用 GCC 原文):

  • -llibrary
  • -l library:指定所需要的額外庫
  • -Ldir:指定庫搜尋路徑
  • -static:靜態連結所有庫
  • -static-libgcc:靜態連結 gcc 庫
  • -static-libstdc++:靜態連結 c++ 庫

2、靜態連結庫的建立和使用

   涉及命令:ar, ar是建立、修改、提取靜態庫的操作。
   ar -t 顯示靜態庫的內容
   ar -d 從庫中刪除成員檔案
   ar -r 在庫中加入成員檔案,若存在,則替換
   ar -c 建立一個庫
   ar -s 無論ar命令是否修改了庫內容,都強制重新生成庫符號表
   步驟如下:
   1、在一個頭檔案種宣告靜態庫所匯出的函式。
   2、在一個原始檔種實現靜態庫所匯出的函式。
   3、編譯原始檔,生成可執行程式碼。
   4、將可執行程式碼所在的目標檔案加入到某個靜態庫中,並將靜態庫拷貝到系統預設的存放庫檔案的目錄下。

下面通過一個例子來說明:mylib.h種存放的是靜態庫提供給使用者使用的函式的宣告,mylib.c實現了mylib.h種宣告的函式。
mylib.h

#ifndef _MYLIB_H_
#define _MYLIB_H_
void weclome(void);
void outString(const char *str);
#endif

mylib.cpp

#include "mylib.h"
void welcome(void){
    printf("welcome to libmylib\n");
}
void outString(const char *str){
    if(str != NULL)
        printf("%s\n", str);
}

test.cpp

#include "mylib.h"
#include 
int main(void){
    printf("create and use library:\n");
    welcome();
    outString("it's successful\n");
    return 0;
}
  1. 編譯mylib.c生成目標檔案:gcc -o mylib.o -c mylib.cpp
  2. 將目標檔案加入到靜態庫中:ar rcs libmylib.a mylib.o
  3. 將靜態庫copy到Linux的庫目錄(/usr/lib或者/lib)下:cp libmylib.a /usr/lib/libmylib.a
  4. 使用靜態庫編譯,編譯時無需帶上字首和字尾:gcc -o test test.cpp -lmylib
  5. 執行可執行程式test: ./test

合併靜態連結庫的指令碼程式碼清單:

 echo CREATE demo.a > ar.mac 
 echo SAVE >> ar.mac 
 echo END >> ar.mac 
 ar -M < ar.mac 
 ar -q demo.a CdtLog.o 
 echo OPEN demo.a > ar.mac 
 echo ADDLIB xml.a >> ar.mac 
 echo SAVE >> ar.mac 
 echo END >> ar.mac 
 ar -M < ar.mac 
 rm ar.mac 

Linux makefile 中使用 ar 指令碼方式進行靜態庫的建立,可以編寫如下程式碼:

 define BUILD_LIBRARY 
 $(if $(wildcard [email protected]),@$(RM) [email protected]) 
 $(if $(wildcard ar.mac),@$(RM) ar.mac) 
 $(if $(filter %.a, $^), 
 @echo CREATE [email protected] > ar.mac 
 @echo SAVE >> ar.mac 
 @echo END >> ar.mac 
 @$(AR) -M < ar.mac 
 ) 
 $(if $(filter %.o,$^),@$(AR) -q [email protected] $(filter %.o, $^)) 
 $(if $(filter %.a, $^), 
 @echo OPEN [email protected] > ar.mac 
 $(foreach LIB, $(filter %.a, $^), 
 @echo ADDLIB $(LIB) >> ar.mac 
 ) 
 @echo SAVE >> ar.mac 
 @echo END >> ar.mac 
 @$(AR) -M < ar.mac 
 @$(RM) ar.mac 
 ) 
 endef 

 $(TargetDir)/$(TargetFileName):$(OBJS) 
    $(BUILD_LIBRARY) 

Linux 靜態庫連結順序問題及解決方法:
為了解決這種庫連結順序問題,我們需要增加一些連結選項 :
(CXX)(LINKFLAGS) (OBJS)Xlinker"("(LIBS) -Xlinker “-)” -o [email protected]
通過將所有需要被連結的靜態庫放入 -Xlinker “-(” 與 -Xlinker “-)” 之間,可以是 g++ 連結過程中, 自動迴圈連結所有靜態庫,從而解決了原本的連結順序問題。

3、共享函式庫的建立和使用

   GNU標準建議所有的函式庫檔案都放在/usr/local/lib目錄下,而且建議命令可執行程式都放在/usr/local/bin目錄下。
   檔案系統層次化標準FHS(Filesystem Hierarchy Standard)規定了在一個發行包中大部分的函式庫檔案應該安裝到/usr/lib目錄下,但是如果某些
庫是在系統啟動的時候要載入的,則放到/lib目錄下,而那些不是系統本身一部分的庫則放到/usr/local/lib下面。
   上面兩個路徑的不同並沒有本質的衝突。GNU提出的標準主要對於開發者開發原始碼的,而FHS的建議則是針對發行版本的路徑的。具體的置資訊可以看
/etc/ld.so.conf裡面的配置資訊,通過對它的修改,可以增加自己的目錄。
   如果你想覆蓋某個庫中的一些函式,用自己的函式替換它們,同時保留該庫中其他的函式的話,你可以在 /etc/ld.so.preload中加入你想要替換的庫
(.o結尾的檔案),這些preloading的庫函式將有優先載入的權ldconfig可以更新/etc/ld.so.cache。/etc/ld.so.cache可以大大提高訪問函式庫的速度。
   HP-UX系統下,就是用SHLIB_PATH這個變數,而在AIX下則使用LIBPATH這個變數。

共享函式庫建立的一個標準命令格式:
gcc -shared -Wl,-soname,your_soname -o library_name file_list library_list

例子:

  • 1、建立Object檔案:
    gcc -fPIC -g -c -Wall a.c
    gcc -fPIC -g -c -Wall b.c
  • 2、建立共享函式庫
    gcc -shared -Wl,-soname,liblusterstuff.so.1 -o liblusterstuff.so.1.0.1 a.o b.o -lc

如果是C++專案,最簡單是使用Cmake來完成共享庫的建立,步驟如下:

  • 如果建立的是JNI連結庫,則需要將 jdk/include/jni.h 和 jdk/include/linux/jni_md.h 複製到 /usr/include 目錄下。反正執行make命令的時候將會報錯

  • 1、確保gcc-c++編譯環境, 安裝命令::
    yum install gcc-c++

  • 2、安裝Cmake
    wget https://cmake.org/files/v3.5/cmake-3.5.1.tar.gz
    tar -xvf cmake-3.5.1.tar.gz
    cd cmake-3.5.1
    ./bootstrap
    make
    make install

  • 3、如果您使用的windows系統,則將您的專案上傳到Linux,進入Linux下該專案的資料夾,建立CMakeLists.txt,內容格式如下:

CMAKE_MINIMUM_REQUIRED(VERSION 2.6)  
# cpp 檔案  
SET(test_SRCS  
    source/test1.cpp  
    source/test2.cpp  
    ......
)  

# 標頭檔案
SET(test_HDRS  
    include/test1.h  
    include/test2.h  
    ..... 
)  

INCLUDE_DIRECTORIES(include)  

# test: 是生產的庫的名字, 這裡可以加上SHARED或者STATIC或者MODULE,分別表示動態庫、靜態庫、模組。不加則預設是靜態庫
ADD_LIBRARY(test SHARED/STATIC/MODULE ${test_SRCS} ${test_HDRS}) 

# 生成可執行檔案
# ADD_EXECUTABLE(test ${test_SRCS} ${test_HDRS})
  • 4、建立動態連結庫:
    ccmake directory #用於配置編譯選項,如VTK_DIR目錄,一般這一步不需要配置
    cmake directory #用於根據CMakeLists.txt生成Makefile檔案
    make #用於執行Makefile檔案,編譯程式,生成可執行檔案

共享函式庫的使用

   一旦你定義了一個共享函式庫,你還需要安裝它。其實簡單的方法就是拷貝你的庫檔案到指定的標準的目錄(例如/usr/lib),然後執行ldconfig。
如果你沒有許可權去做這件事情, 那麼最簡單的方法就是執行ldconfig:
    ldconfig -n directory_with_shared_libraries 
    然後設定LD_LIBRARY_PATH這個環境變數,它是一個以逗號分隔的路徑的集合:
    LD_LIBRARY_PATH=$LD_LIBRARY_PATH,my_program
    如果一個新版的函式庫要和老版本的二進位制的庫不相容,則soname需要改變。對於C語言,有四種情況會出現不相容問題:
   · 一個函式的行文改變了,這樣它就可能與最開始的定義不相符合。
   · 輸出的資料項改變了。
   · 某些輸出的函式刪除了。
   · 某些輸出函式的介面改變了。**

4、共享函式庫的動態載入

   共享函式庫可以在程式執行過程中的任何時間載入,它們特別適合在函式中載入一些模組和plugin擴充套件模組的場合,因為它可以在當程式需要某個
plugin模組時才動態的載入。
   Linux系統下,DL函式庫與其他函式庫在格式上沒有特殊的區別。通常C語言環境下,需要包含這個標頭檔案。 Linux中使用的函式和Solaris中一樣,都是
dlpoen()API。當然不是所有的平臺都使用同樣的介面,例如HP-UX使用shl_load()機制,而Windows平臺用另外的其他的呼叫介面。

dlopen()

   dlopen函式開啟一個函式庫然後為後面的使用做準備。C語言原形是: 
     void * dlopen(const char *filename, int flag);
   如果檔名filename是以“/”開頭,也就是使用絕對路徑,那麼dlopne就直接使用它,而不去查詢某些環境變數或者系統設定的函式庫所在的目錄了。
否則dlopen()就會按照下面的次序查詢函式庫檔案:
   1. 環境變數LD_LIBRARY指明的路徑。
   2. /etc/ld.so.cache中的函式庫列表。
   3. /lib目錄,然後/usr/lib。不過一些很老的a.out的loader則是採用相反的次序,也就是先查 /usr/lib,然後是/lib。
   dlopen()函式中,引數flag的值必須是RTLD_LAZY或者RTLD_NOW,RTLD_LAZY的意思是resolve undefined symbols as code from the dynamic library is executed,而RTLD_NOW的含義是resolve all undefined symbols before dlopen() returns and fail if this cannot be done'
   注意函式庫的載入順序。

dlerror()

    通過呼叫dlerror()函式,我們可以獲得最後一次呼叫dlopen(),dlsym(),或者dlclose()的錯誤資訊。 

dlsym()

   void * dlsym(void *handle, char *symbol);
   函式中的引數handle就是由dlopen開啟後返回的控制代碼,symbol是一個以NIL結尾的字串。
   如果dlsym()函式沒有找到需要查詢的symbol,則返回NULL。典型的呼叫過程如下:
dlerror();      /*clear error code */  
s = (actual_type)dlsym(handle,    symbol_being_searched_for);  
if((error = dlerror()) != NULL){  
    /* handle error, the symbol wasn't found */  
} else {  
    /* symbol found, its value is in s */  
}    

dlclose()
dlopen()函式的反過程就是dlclose()數,dlclose()函式用力關閉一個DL函式庫。真正釋放的時候,如果函式庫裡面有_fini()這個函式,則自動呼叫_fini()這個函式,做一些必要的處理。Dlclose()返回0表示成功,其他非0值表示錯誤。

動態函式庫的建立:
動態函式庫並非另外一種庫函式格式,可只是在程式執行的任何時候動態的載入的共享函式庫或。它的建立可以參考共享函式庫的建立。

動態函式庫的使用:

int main(int argc, char *argv){  
        void *handle;  
        char *error;  

        double (*cosine )(double);  
        handle = dlopen("/lib/libm.so.6", RTLD_LAZY);  
        if(!handle){  
            fputs(dlerror(), stderr);  
             exit(1);  
        }  

        cosine = dlsym(handle, "cos");  
        if((error = dlerror()) != NULL){  
            fputs(error, stderr);  
            exit(1);  
        }  

        printf("%f", (*cosine)(2, 0));  

        dlclose(handle);  

        return 0;  
}  

如果這個程式名字叫test.c,那麼用下面的命令來編譯:
gcc -o test test.c –ldl

相關推薦

Linux動態連結建立使用及C呼叫matlab動態問題

其實這個資料網路上已經很多了,但是還是有一些細節讓我搗鼓了很久,以及最近在做matlab mcc做成so檔案供給c++呼叫的時候的一些問題。 一、首先如何製作Linux下的so 檔案 【1】http://bbs.chinaunix.net/thread-1281954-1-

Linux動態連結建立使用

1、連結庫的基本知識 庫是一種軟體元件技術,庫裡面封裝了資料和函式。它的使用,可以是程式模組化。在程式中使用,我們可以稱之為程式函式庫。 程式函式庫可分為3種類型:靜態函式庫(static libraries)、共享函式庫(shared lib

Linux學習筆記 三 linux連結以及實現

1、連結庫概述 Linux下得庫有動態與靜態兩種,動態通常用.so為字尾,靜態用.a為字尾。面對比一下兩者:     靜態連結庫:當要使用時,聯結器會找出程式所需的函式,然後將它們拷貝到執行檔案,由於這種拷貝是完整的,所以一旦連線成功,靜態程式庫也就不再需要了。

Linux動態連結的版本號以及ldconfig

動態連結庫的三個名字 1. realname, 真正的名字,一般情況下如果你有版本,應該在後面加上lib[libraryname].so.[version] eg: libtest.so.1.0.0 2. soname, 在編譯動態庫的時候指定的名字,這個名字將會被新增到

linux 新增動態連結路徑

1 2 export LD_LIBRARY_PATH=你的庫的路徑:$LD_LIBRARY_PATH echo $LD_LIBRARY_PATH linux 預設回去/lib和/usr/lib目錄下查詢庫,可以通過ln建立軟連線 轉:

五十五、windowsLinux虛擬環境的建立使用

一、問題: 安裝同一個包的不同版本,後安裝的包會把原來安裝的包覆蓋掉。這樣,如同一臺機器上兩個專案依賴於相同包的不同版本, 則會導致一些專案執行失敗。 解決的方案就是:虛擬環境。 虛擬環境是真實

動態連結dll靜態連線lib的區別

          由於專業原因,一直沒有系統的學習過c++。最近在學習opencv,而opencv的配置對於程式設計經驗不豐富的人來說理解起來還是需要一個過程的。opencv配置的設定涉及到兩個很重要的概念:dll和lib。          首先什麼是dll和lib?

linux靜態連結存在,但是在連結過程會出現undefined reference的錯誤

如題,使用linux編譯程式時,需要靜態連結庫。 在連結過程也已指定靜態庫的路徑及庫名,且連結器能找到指定的庫,但會提示庫中被呼叫的函式undefined reference 這是需要檢查連結庫在連結命令中的位置,要保證依賴該庫的中間檔案或庫在它的前面。 即若一個程式需要l

靜態連結的編譯與使用 linux動態連結靜態連結到底是個什麼鬼?(一)靜態連結的編譯與使用

linux下的動態連結庫和靜態連結庫到底是個什麼鬼?(一)靜態連結庫的編譯與使用       知識不等於技術,這句話真的是越工作的時間長越深有體會,學習到的知識只有不斷的實踐,才成真正在自已的心裡紮下根,成為自身的一部分,所以無論如何,我希望我的部落格可以

Linux動態(.so)靜態(.a) 的區別 Linux動態(.so)靜態(.a) 的區別 動態(.so)連結靜態(.a)的情況總結

Linux下動態庫(.so)和靜態庫(.a) 的區別   靜態庫在程式編譯時會被連線到目的碼中,程式執行時將不再需要該靜態庫。編譯之後程式檔案大,但載入快,隔離性也好。 動態庫在程式編譯時並不會被連線到目的碼中,而是在程式執行是才被載入,因此在程式執行時還需要動態庫存在。多個

Linux使用QT編寫呼叫動態連結(.so檔案)

Linux下Qt建立和呼叫共享庫檔案.so 費了點功夫,總算get了編寫共享庫(Shared Library,Windows下稱為“動態連結庫”,即Dynamic Link Library)和呼叫的這個新技能! 動態連結庫的好處是不言而喻的,一個稍微複雜一點的程式,頂層設計的時候良好

linux封裝函式——動態.so靜態.a(程式碼實現及連結方式)

在linux環境下的連結庫分為靜態連結庫(.a庫)和動態連結庫(.so庫),其作用是把C程式編譯好做成一種可執行連結檔案,主程式檔案呼叫這些程式的函式介面是可以使用a庫或so庫,在主程式中只需要include含有庫中提供的函式介面宣告的標頭檔案即可。所以學會如何

Linux動態靜態連結

一、檢視連結了哪些指令 ldd 程式名字 二、在應用程式需要連線外部庫的情況下,linux預設對庫的連線是使用動態庫,在找不到動態庫的情況下再選擇靜態庫。使用方式為: gcc test.cpp -L. -ltestlib 如果當前目錄有兩個庫libtestlib.

Linux使用gcc進行靜態編譯使用動態連結編譯

/home/plus/demo下有main.c和func.c兩個檔案: func.c: int func(int a) { return a+1; } main.c: #i

linux生成,使用靜態動態連結

當要使用靜態的程式庫時,聯結器會找出程式所需的函式,然後將它們拷貝到執行檔案,由於這種拷貝是完整的,所以一旦連線成功,靜態程式庫也就不再需要了。 然而,對動態庫而言,就不是這樣。動態庫會在執行程式內留下一個標記‘指明當程式執行時,首先必須載入這個庫。由於動態庫節省空間,li

linux的靜態動態

列表 可執行文件 運行時 打包 文件的 ade 命令 div library 一、linux下的靜態庫 靜態庫中的被調用的函數的代碼會在編譯時一起被復制到可執行文件中去的!!可執行文件在運行不需要靜態庫的存在! 二、linux下動態庫的構建和使用 1、動態庫的構建

動態連結(DLL)的建立使用

最近想做個記錄日誌的C++庫,方便後續使用。想著使用動態庫,正好沒用過,學習下。概念這裡不贅述。學習過程中碰到的幾點,記錄下來。學習是個漸進的過程,本文也是一個逐漸完善的過程。 一、Static Library 標準Turbo 2.0中的C函式庫(scanf、pringf、memc

動態連結隱式建立呼叫

1.建立 我選的WIN32控制檯,下一步,勾DLL選項。 在專案新建一個類,在類前面加__declspec(dllimport) #define MYDLL  __declspec(dllexport)#else#define MYDLL  __declspec(dl

Linux中,.a.so,其實就是靜態連結動態連結

詳細查了一下,.a與.so的區別,其實就是靜態連結庫與動態連結庫。有一篇博文,很詳細,附上鍊接:http://blog.csdn.net/nieyinyin/article/details/6890557   Linux下的.so是基於Linux下的動態連結,其功能和作用類

Linux執行時呼叫動態連結.so的三種方法(筆記)

在/etc/ld.so.conf.d/下建立xxx.conf,在文字中加入.so所在路徑,如:/usr/xxx等等,然後使用相應ldconfig命令使之生效。 將.so所在路徑新增為LD_LIBRARY_PATH環境變數。 在編譯命令中使用-Wl,-rpath