1. 程式人生 > >動態庫的使用方法

動態庫的使用方法

在編寫程式時,動態庫是常常用到的工具,在vs等程式設計環境下只需要,完成工程的本地化配置或是直接將dll配置到環境變數即可(不推薦,畢竟小題大做了),而對於像樓主這樣剛剛接觸Linux作業系統的菜鳥來說,配置動態(共享)庫還不是一個簡單的問題。

在介紹動態庫的呼叫方法之前,先介紹一下動態庫的編譯(如何生成so檔案)

需要一個包含幾個方法宣告的標頭檔案和對應的定義檔案。例如

//so_test.h 標頭檔案
#include "stdio.h"
void test_a();
void test_b();
void test_c();
//test_a.c:
#include "so_test.h"
void test_a() { printf("this is in test_a...\n"); }
//test_b.c:
#include "so_test.h"
void test_b()
{
  printf("this is in test_b...\n");
}
#include "so_test.h"
void test_c()
{
  printf("this is in test_c...\n");
}

將上述so_test.h test_a.c test_b.c test_c.c 檔案放與同一目錄下,執行

gcc test_a.c test_b.c test_c.c
-fPIC -shared -o libtest.so

這裡寫圖片描述
此時,我們可以看到 多出了一個libtest.so檔案,這裡的命名有些講究,應該命名為lib+xxxx+.so後面再解釋為什麼,這就是我們剛剛生成的共享庫檔案。
-fPIC:表示編譯為位置獨立的程式碼,不用此選項的話編譯後的程式碼是位置相關的所以動態載入時是通過程式碼拷貝的方式來滿足不同程序的需要,而不能達到真正程式碼段共享的目的(好像不是必須的哦)

-shared : 該選項指定生成動態連線庫,這個是必須的

到這來似乎完成了共享庫的編寫,那麼我們先來簡單的介紹下兩種呼叫方式:
①隱式呼叫
②顯示呼叫

簡單來時,隱式呼叫就是指在我們的程式程式碼中,沒有定義我們呼叫了哪個共享庫,只是包含了某個貢獻庫的標頭檔案而已,而在編譯的時候需要指明共享庫。我們繼續用示例說明:

#include "so_test.h"
int main()
{
test_a();
test_b();
test_c();
return 0;
}

很簡單的呼叫,和普通的連結沒什麼區別啊!是的不過在編譯的時候要花費一些功夫

 gcc test.c -L. -ltest -o test

-L. : 表示需要連結的共享庫在當前目錄;

-Itest : 表示呼叫的共享庫的檔名為 lib+test+.so 即libtest.so

我們執行一下生成的test檔案
這裡寫圖片描述

無法開啟shared object file

為什麼不行,別急 我們藉助ldd命令檢視下 test檔案的連結情況

ldd text

這裡寫圖片描述
我們剛剛寫的libtest.so 就和你放在一個資料夾居然說找不到,服了(:зゝ∠)

話說連結庫可以從以下五個位置檢索
①環境變數LD_LIBRARY_PATH列出的用分號間隔的所有目錄;
②檔案/etc/ld.so.cache中找到的庫的列表,由ldconfig命令重新整理;
③目錄usr/lib;
④目錄/lib;
⑤當前目錄;

我建議還是將我們生成的共享庫拷貝到/usr/lib/或/lib目錄下,修改LD_LIBRARY_PATH不是和windows環境下改環境變數一樣小題大做嗎?(個人看法請大神指正)

cp libtest.so /usr/lib/

這裡寫圖片描述

現在我們發現他能找到我們寫的共享庫了,也就能正常運行了。

好的接下來看看顯示呼叫

此時我們已經寫好了libtest.so共享庫,並且已拷貝至/usr/lib/目錄下

顯示呼叫,就是在程式碼中明確指明我們需要呼叫的共享庫,而不需要在編譯時指明,因此我們需要一些特殊的函式完成上述功能。

我們需要用到一個頭檔案dlfcn.h 和其對應的四個函式。下面在介紹具體步驟再加以說明。
①把dlfcn.h系統標頭檔案包含進來
②用dlopen()函式開啟庫檔案,並指定開啟方式;
dllope()有兩個引數
第一個引數為共享庫的名稱,能取到的位置不在累述了。
第二個引數為開啟共享庫的方式。有兩個取值
①RTLD_NOW:將共享庫中的所有函式載入到記憶體
②RTLD_LAZY:會推後共享庫中的函式的載入操作,直到呼叫dlsym()時方載入某函式
返回值是一個void *指標
③用dlerror()函式測試是否開啟成功,並進行錯誤處理,返回值是一個char *;
④用dlsym獲得函式地址,存放在一個函式指標中,呼叫格式是dlsym(void *,”fcnname”);
⑤用獲得的函式指標進行函式呼叫。
⑥程式結束時用dlclose(void *)關閉開啟的動態庫,防止資源洩露。

#include <dlfcn.h>         //包含標頭檔案
#include "so_test.h"       //包含共享庫標頭檔案
int main(int argc,char *argv[])
{
   void(*pT)();            //申請一個與共享庫對應的函式指標
   void *Handle=dlopen("libtest.so",RTLD_LAZY);
                           //開啟共享庫,並有void指標 Handle儲存
   if(Handle==NULL)        //檢測是否開啟成功
   {
       printf("Fail load library\n");
       return -1;
   }
   char * pE=dlerror();    //檢測函式是否開啟成功
   if(pE!=NULL)
   {
       printf("%s\n",pE);
       return -1;
   }
   pT=dlsym(Handle,"test_a");    //呼叫具體的函式
   pE=dlerror();                 //再次檢測函式開啟是否成功
   if(pE!=NULL)
   {
       printf("%s\n",pE);
       return -1;
   }
   (*pT)();                      //呼叫共享庫的函式
   dlclose(Handle);              //關閉共享庫

   return 0;
}

編譯命令為

gcc -o main -ldl test.c 

-ldl : 指明生成物件需要使用共享庫,但不用具體指明哪個共享庫

這裡寫圖片描述

(^o^)/~ 呼叫動態(共享庫)的方法就先說到這來嘍!!