動態(tài)庫與靜態(tài)庫--函數(shù)篇

  • 作用
  1. 分離編譯
  2. 代碼重用
  • 分類
分類 作用 后綴
靜態(tài)庫 一個或多個.o目標(biāo)文件歸檔在一個文件中 .a
共享庫 沒有main函數(shù)的可執(zhí)行文件 .so
動態(tài)加載庫 沒有main函數(shù)的可執(zhí)行文件,接口復(fù)合API .so

0.前提(文件內(nèi)容)

  • test.cpp
#include <iostream>
#include "test.h"
using namespace std;
void Func(int i){
    cout << __func__ << "(" << i << ")" << endl;
}
  • test.h
#ifndef __TEST_H_
#define __TEST_H_
void Func(int i);
#endif
  • main.cpp
#include "test.h"
int main(){
    Func();
    return 0;
}

1. 靜態(tài)庫的制作與使用

1.1 創(chuàng)建

  • 編譯源文件
g++ -c -o test.o test.cpp
  • 生成靜態(tài)庫
ar -rcs libtest.a test.o
  • ar選項(xiàng)
命令選項(xiàng) 作用
r 替換模塊(replace)
c 創(chuàng)建庫(create)
s 建立索引

tarar都是歸檔工具

  • tar用于創(chuàng)建.tar歸檔文件。
  • ar用于創(chuàng)建歸檔文件,并且為歸檔的目標(biāo)文件中的符號建立索引。
  • 查看目標(biāo)文件的符號(symbol)信息
nm 目標(biāo)文件

目標(biāo)文件可以是.o.a,也可以是可執(zhí)行文件。

1.2 使用

  • 鏈接靜態(tài)庫
g++ -o main main.cpp -L. -ltest

或者

g++ -o main main.cpp ./libtest.a

注意:庫一定要放在命令行的末尾

  • 測試
./main
  • 結(jié)果
Func(100)

2. 共享庫的制作

2.1 創(chuàng)建

  1. 編譯目標(biāo)文件
g++ -c -fPIC test.cpp -o test.o
  1. 生成動態(tài)庫
g++ -shared test.o -o libtest.so

以上兩步可以合并為g++ -shared -fPIC -o libtest.so test.cpp

  • 選項(xiàng)說明
命令選項(xiàng) 作用
shared 創(chuàng)建動態(tài)庫
fPIC 代碼都是與位置無關(guān)的

每個共享函數(shù)庫都有個特殊的名字,稱作soname。soname名字命名必須以lib作為前綴,然后是函數(shù)庫的名字,然后是.so,最后是版本號信息。不過有個特例,就是非常底層的C庫函數(shù)都不是以lib開頭這樣命名的。

2.2 使用

  1. 生成可執(zhí)行文件
g++ -o main main.cpp -L. -ltest

或者

g++ -o main main.cpp ./libtest.so

注意:庫一定要放在命令行的末尾

  1. 測試
    指定動態(tài)鏈接庫位置
export LD_LIBRARY_PATH=動態(tài)鏈接庫位置

執(zhí)行

./main
  1. 結(jié)果
Func(100)

關(guān)于動態(tài)鏈接庫的安裝路徑

  • 如果安裝在/lib或者/usr/lib下,那么ld默認(rèn)能夠找到,無需其他操作。
  • 如果安裝在其他目錄,需要將其添加到/etc/ld.so.cache文件中,步驟如下:
    編輯/etc/ld.so.conf文件,加入庫文件所在目錄的路徑
    運(yùn)行ldconfig ,該命令會重建/etc/ld.so.cache文件

當(dāng)靜態(tài)庫和動態(tài)庫同名時, gcc命令將優(yōu)先使用動態(tài)庫。

  • 查看執(zhí)行文件鏈接的動態(tài)鏈接庫
ldd 可執(zhí)行文件

也可以查看動態(tài)鏈接庫所鏈接的其它動態(tài)庫。

3. 動態(tài)加載庫

3.1 創(chuàng)建

  1. 修改test.h
#ifndef __TEST_H_
#define __TEST_H_

#ifdef __cpluscplus 
extern "C"  //C++
{  
#endif 

void Func(int i);

#ifdef __cpluscplus  
}
#endif 

#endif // __TEST_H_
  • extern關(guān)鍵字:在一個項(xiàng)目中必須保證函數(shù)、變量、枚舉等在所有的源文件中保持一致,除非你指定定義為局部的。
    extern "C"指令中的C,表示的一種編譯和連接規(guī)約,而不是一種語言。C表示符合C語言的編譯和連接規(guī)約的任何語言
    extern "C"的真實(shí)目的是實(shí)現(xiàn)類C和C++的混合編程。在C++源文件中的語句前面加上extern "C",表明它按照類C的編譯和連接規(guī)約來編譯和連接,而不是C++的編譯的連接規(guī)約。
  • __cplusplus的值是為了表示C++的版本,目前不應(yīng)該依賴該宏的值。
  1. 修改main.cpp
#include <iostream>
#include <cstdlib>
#include <dlfcn.h>
using namespace std;
int main(){
    void *so_handle = dlopen("./libtest.so", RTLD_LAZY); // 加載.so文件
    if (!so_handle) {
        cerr << "Error: load so failed" << endl;
        exit(-1);
    }
    typedef void func_t(int);
    func_t *pFunc = (func_t *)dlsym(so_handle,"Func");
    char *err = dlerror();
    if (NULL != err) {
        cerr << "load Func err:" << err << endl;
        exit(-1);
    }
    pFunc(100);
    dlclose(so_handle); // 關(guān)閉so句柄
    return 0;
}
  1. 重新編譯.so
g++ -shared -fPIC test.cpp -o libtest.so

3.2 使用

  1. 重新編譯main.cpp
g++ -o main main.cpp -ldl
  1. 執(zhí)行
.\main
  1. 結(jié)果
Func(100)

3.3 動態(tài)庫(共享庫、動態(tài)加載庫)與靜態(tài)庫的區(qū)別

  • 靜態(tài)庫在程序編譯時會被連接到目標(biāo)代碼中,程序運(yùn)行時將不再需要該靜態(tài)庫,因此體積較大。
  • 動態(tài)庫在程序編譯時并不會被連接到目標(biāo)代碼中,而是在程序運(yùn)行是才被載入,因此在程序運(yùn)行時還需要動態(tài)庫存在,因此代碼體積較小。

3.4 動態(tài)加載與靜態(tài)加載的區(qū)別

  • 動態(tài)加載
  1. 靈活,可以在需要的時候進(jìn)行加載,在不需要的時候進(jìn)行卸載,這樣可以不必占用內(nèi)存。
  2. 可以在沒有動態(tài)庫時候發(fā)現(xiàn),而不致程序報(bào)錯。
  3. 加載程序中有條件才運(yùn)行的庫。
  4. 熱更新,在不停止程序的前提下進(jìn)行更新。
  5. 復(fù)雜一些,需要顯示獲得函數(shù)地址。
  • 靜態(tài)加載
  1. 簡單方便
  2. 沒有找到動態(tài)庫時,系統(tǒng)報(bào)錯
  3. 加載運(yùn)行很久的庫

4. 總結(jié)

靜態(tài)庫、共享庫與動態(tài)庫編譯鏈接使用比較
靜態(tài)庫與動態(tài)庫Make file比較

5. 補(bǔ)充

使用動態(tài)庫的方法還有如下幾種

  • 方法一:連接前,添加動態(tài)庫目錄到環(huán)境變量LD_RUN_PATH。

    export LD_RUN_PATH=動態(tài)庫目錄
    
  • 方法二:編譯鏈接時,添加鏈接選項(xiàng)-Wl,-rpath -Wl,動態(tài)庫目錄

  • 方法三:執(zhí)行前,添加動態(tài)庫目錄到環(huán)境變量LD_LIBRARY_PATH。

    export LD_LIBRARY_PATH=動態(tài)庫目錄
    
  • 方法四:添加共享庫目錄/usr/local/lib到共享庫配置文件

    echo 動態(tài)庫目錄 >> /etc/ld.so.conf
    ldconfig
    

6. 參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容