天天看點

Linux下動态庫和靜态庫制作與調用

Linux下動态庫和靜态庫制作與調用

1.動态庫和靜态庫簡介

   靜态庫是指在應用中,有一些公共代碼需要反複使用,就把這些代碼編譯為“庫”檔案;在連結步驟中,連接配接器将從庫檔案取得所需的代碼,複制到生成的可執行檔案中。這種庫稱為其特點是可執行檔案中包含了庫代碼的一份完整拷貝;缺點就是被多次使用就會有多份備援拷貝。

      動态庫又稱動态連結庫英文為DLL,是指DynamicLinkLibrary 的縮寫形式,DLL是一個包含可由多個程式同時使用的代碼和資料的庫,DLL不是可執行檔案。動态連結提供了一種方法,使程序可以調用不屬于其可執行代碼的函數。

      靜态庫:在編譯的時候加載生成目标檔案,在運作時不用加載庫,在運作時對庫沒有依賴性。

      動态庫:在目标檔案運作時加載,對庫有依賴性。

 1.1 Linux下動态庫和靜态庫指令方式

  動态庫命名方式:libxxx.so。其中so是shared objecd的縮寫,即可以共享的目标檔案,lib為庫的固定格式,xxx為庫名稱,.so為動态庫字尾。

  動态庫命名方式:libxxx.a xxx靜态庫名稱。Linux 上的靜态庫,其實是目标檔案的歸檔檔案。

 1.2 編譯生成共享庫示例

  指令:gcc -fPIC -shared -o libxxx.so xxx.c

      此過程分為編譯和連結兩部分,-fPIC是編譯選項,PIC表示要生成位置無關的代碼,這是動态庫的特性,-shared是連結選項,告訴gcc生成動态庫而不是可執行檔案。

    上述指令等同于:

    gcc -fPIC -c xxx.c

    gcc -shared -o libxxx.so xxx.o

   如下代碼為例實作動态庫編譯與調用:

Linux下動态庫和靜态庫制作與調用
[wbyq@wbyq shared]$ gcc -fPIC -shared -o libmyfile.so ./src/*.c -I./include      

  gcc -I  指定頭文路徑

 1.3 動态庫調用

[wbyq@wbyq shared]$ gcc main.c -Iinclude -lmyfile -L./lib      

   gcc -L 指定動态庫路徑

   gcc -l 指定動态庫名字

 1.4 程式運作

[wbyq@wbyq shared]$ ./a.out 
./a.out: error while loading shared libraries: libmyfile.so: cannot open shared object file: No such file or directory      

  錯誤原因:

  系統動态庫預設搜尋路徑: /lib 和 /usr/lib

  解決辦法1:将libmyfile.so 拷貝到/usr/lib或者/lib目錄下。

  解決辦法2:修改系統環境變量,動态庫環境變量:LD_LIBRARY_PATH

  修改環境變量示例:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/mnt/hgfs/ubuntu/shared/lib      

  注意:在指令行修改環境變量隻對目前終端有效。

  解決辦法2:修改系統啟動檔案

  Ubuntu下開機普通使用者啟動檔案:~/.bashrc ~/.profile

  管理者使用者:/.profile

  修改~/.bashrc檔案:

Linux下動态庫和靜态庫制作與調用

  同步檔案:source ~/.bashrc

2 linux靜态庫建立與調用

 2.1 linux靜态庫的指令規則

  靜态庫libxxx.a   xxx靜态庫名稱。Linux 上的靜态庫,其實是目标檔案的歸檔檔案。

 2.2 linux靜态庫建立步驟

  (1)編寫源檔案,通過gcc -c生成目标檔案。

  (2)用ar歸檔目标檔案,生成靜态庫。

  (3)配合靜态庫,寫一個使用靜态庫中函數的頭檔案。

  (4).使用靜态庫時,在源碼中包含對應頭檔案,連結是記得連結自己的庫。

 2.3 生成靜态庫

[wbyq@wbyq shared]$ gcc -c src/*.c -Iinclude  #生成 .o檔案
[wbyq@wbyq shared]$ ar crv -o ./lib/libmyfile2.a ./*.o #生成靜态庫
a - ./my_cat.o
a - ./my_cp.o
a - ./my_du.o      

 2.4 調用靜态庫和檢視動态庫資訊

[wbyq@wbyq shared]$ ar t ./lib/libmyfile2.a  #檢視靜态庫資訊
my_cat.o
my_cp.o
my_du.o
[wbyq@wbyq shared]$ gcc main.c -L./lib -lmyfile2 -o app -Iinclude #調用靜态      

3.gcc編譯器常用選項

 3.1 gcc基本用法

  使用 gcc編譯器的時候,我們必須給出一系列必要的調用參數和檔案名稱。 GCC 編譯器的調用參數大約有100 多個,這裡隻介紹其中最基本、最常用的參數。

  gcc 最基本用法:gcc [參數] [檔案名稱]

​ 3.2 常用參數

 -c 隻編譯:不連結成為可執行檔案,編譯器隻是由輸入的.c 等源代碼檔案生成.o 為字尾的目标檔案,通常用于編譯不包含主程式的子程式檔案。

 -o output_filename:确定輸出檔案的名稱為 output_filename,同時這個名稱不能和源檔案同名。如果不給出這個選項, gcc 就給出預設的可執行檔案 a.out。

 -g:産生符号調試工具(GNU 的 gdb)所必要的符号資訊,要想對源代碼進行調試,我們就必須加入這個選項。

 -O:對程式進行優化編譯、連結,采用這個選項,整個源代碼會在編譯、連結過程中進行優化處理,這樣産生的可執行檔案的執行效率可以提高,但是,編譯、連結的速度就相應地要慢一些。

 -O2:比-O 更好的優化編譯、連結,當然整個編譯、連結過程會更慢。

 -E:僅執行編譯預處理;

 -S:将 C 代碼轉換為彙編代碼;

 3.3 編譯時指定庫檔案和頭檔案路徑

 -L:指定動态庫路徑(可以指定多個路徑)。

         示例: gcc test.c -o app -L/usr/lib -L ./lib

 -I:指定頭檔案存放的路徑(可以指定多個路徑)。

       示例: gcc test.c -I ./ -I /include

 -l:指定庫名稱(可以指定多個路徑)。

       示例: gcc test.c -l my_test -l func

4.linux下采用dlopen調用動态庫

 4.1 dlopen調用動态庫意義

      為了使程式友善擴充,具備通用性,可以采用插件形式。采用異步事件驅動模型,保證主程式邏輯不變,将各個業務已動态連結庫的形式加載進來,這就是所謂的插件。 linux 提供了加載和處理動态連結庫的系統調用,非常友善。

 4.2 dlopen系列函數介紹

#include <dlfcn.h>
void *dlopen(const char *filename, int flags);
函數功能:打開動态庫
形參:filename --動态庫路徑+名字 例:./lib/libmyfile.so
   flags --RTLD_LAZY使用時解析(暫緩解析)
   RTLD_NOW --立刻解析
傳回值: handle --成功傳回庫引用資訊,失敗傳回NULL      
int dlclose(void *handle);
函數功能:關閉動态庫
形參:handle --dlopen函數傳回值      
void *dlsym(void handle, const charsymbol);
函數功能:符号解析
形參:handle --dlopen函數傳回值
   symbol --要解析的符号
傳回值:成功傳回符号位址,
    失敗傳回NULL      
char *dlerror(void); //列印錯誤資訊      

 4.3函數解析示例

#include "./include/my_file.h"
#include <dlfcn.h>
#include <stdio.h>
int main()
{
    int (*p)(const char *);//定義一個函數指針

    /*打開動态庫*/
    void *handle=dlopen("./lib/libmyfile.so",RTLD_LAZY);
    if(handle==NULL)
    {
        printf("動态庫解析失敗%s\n",dlerror());//列印錯誤資訊
        return 0;
    }
    /*符号解析*/
    p=dlsym(handle,"my_cat");
    int res=p("main.c");
    printf("res=%d\n",res);

    p=dlsym(handle,"my_du");
    p("main.c");
    //typedef unsigned int u32;
    typedef int (*my_cp)(const char *,const char *);//my_cp表示函數指針類型
    my_cp test;//定義函數指針變量
    test=dlsym(handle,"my_cp");
    test("main.c","test.c");

    dlclose(handle);//關閉動态庫
}      

 4.4變量解析示例

#include "./include/my_file.h"
#include <dlfcn.h>
#include <stdio.h>
int main()
{
    int (*p)(const char *);//定義一個函數指針
    /*打開動态庫*/
    void *handle=dlopen("./lib/libmyfile.so",RTLD_LAZY);
    if(handle==NULL)
    {
        printf("動态庫解析失敗%s\n",dlerror());//列印錯誤資訊
        return 0;
    }
    /*符号解析*/
    p=dlsym(handle,"my_du");
    p("main.c");
    printf("-----------------------------------\n");
    /*解析變量*/
    int *p2;
    p2=dlsym(handle,"data");
    printf("*p2=%d\n",*p2);
    *p2=300;
    p("main.c");
    dlclose(handle);//關閉動态庫
}      

繼續閱讀