采用dlopen、dlsym、dlclose加載動態鏈接庫


1、前言

  為了使程序方便擴展,具備通用性,可以采用插件形式。采用異步事件驅動模型,保證主程序邏輯不變,將各個業務已動態鏈接庫的形式加載進來,這就是所謂的插件。linux提供了加載和處理動態鏈接庫的系統調用,非常方便。本文先從使用上進行總結,涉及到基本的操作方法,關於動態鏈接庫的本質及如何加載進來,需要進一步學習,后續繼續補充。如何將程序設計為插件形式,挖掘出主題和業務之間的關系,需要進一步去學習。

2、生產動態鏈接庫

編譯參數 gcc -fPIC -shared 

例如將如下程序編譯為動態鏈接庫libcaculate.so,程序如下:

復制代碼
int add(int a,int b) { return (a + b); } int sub(int a, int b) { return (a - b); } int mul(int a, int b) { return (a * b); } int div(int a, int b) { return (a / b); }
復制代碼

編譯如下: gcc -fPIC -shared caculate.c -o libcaculate.so 

3、dlopen、dlsym函數介紹

      在linux上man dlopen可以看到使用說明,函數聲明如下:

復制代碼
#include <dlfcn.h>

void *dlopen(const char *filename, int flag); char *dlerror(void); void *dlsym(void *handle, const char *symbol); int dlclose(void *handle);
復制代碼

  dlopen以指定模式打開指定的動態連接庫文件,並返回一個句柄給調用進程,dlerror返回出現的錯誤,dlsym通過句柄和連接符名稱獲取函數名或者變量名,dlclose來卸載打開的庫。 dlopen打開模式如下:

  RTLD_LAZY 暫緩決定,等有需要時再解出符號 
  RTLD_NOW 立即決定,返回前解除所有未決定的符號。

采用上面生成的libcaculate.so,寫個測試程序如下:

復制代碼
 1 #include <stdio.h>  2 #include <stdlib.h>  3 #include <dlfcn.h>  4  5 //動態鏈接庫路徑  6 #define LIB_CACULATE_PATH "./libcaculate.so"  7  8 //函數指針  9 typedef int (*CAC_FUNC)(int, int); 10 11 int main() 12 { 13 void *handle; 14 char *error; 15 CAC_FUNC cac_func = NULL; 16 17 //打開動態鏈接庫 18 handle = dlopen(LIB_CACULATE_PATH, RTLD_LAZY); 19 if (!handle) { 20 fprintf(stderr, "%s\n", dlerror()); 21  exit(EXIT_FAILURE); 22  } 23 24 //清除之前存在的錯誤 25  dlerror(); 26 27 //獲取一個函數 28 *(void **) (&cac_func) = dlsym(handle, "add"); 29 if ((error = dlerror()) != NULL) { 30 fprintf(stderr, "%s\n", error); 31  exit(EXIT_FAILURE); 32  } 33 printf("add: %d\n", (*cac_func)(2,7)); 34 35 cac_func = (CAC_FUNC)dlsym(handle, "sub"); 36 printf("sub: %d\n", cac_func(9,2)); 37 38 cac_func = (CAC_FUNC)dlsym(handle, "mul"); 39 printf("mul: %d\n", cac_func(3,2)); 40 41 cac_func = (CAC_FUNC)dlsym(handle, "div"); 42 printf("div: %d\n", cac_func(8,2)); 43 44 //關閉動態鏈接庫 45  dlclose(handle); 46  exit(EXIT_SUCCESS); 47 }
復制代碼

編譯選項如下:gcc -rdynamic -o main main.c -ldl

測試結果如下所示:

 

參考資料:

http://blog.chinaunix.net/uid-26285146-id-3262288.html

http://www.360doc.com/content/10/1213/22/4947005_77867631.shtml

 

 

dlopen 與dlsym

 Dynamically Loaded (DL)

在讀HAL相關源碼的時候發現這兩個函數 

簡而言之,共享對象通過dlopen動態打開動態庫的加載完成后,返回一個句柄,通過dlsym定位到你需要執行的函數指針然后可以在程序中使用

dlopen -- open a dynamically linked library

dlsym -- get the address of a symbol in a dynamically linked library

例子void *handle;

     int i, *iptr;

     int (*fptr)(int);

     /* open the needed object */

     handle = dlopen("/usr/mydir/libx.so", RTLD_LAZY);

     /* find address of function and data objects */

     fptr = (int (*)(int))dlsym(handle, "some_function");

     iptr = (int *)dlsym(handle, "int_object");

     /* invoke function, passing value of integer as a parameter */

     i = (*fptr)(*iptr);

參考資料

http://topic.csdn.net/u/20090422/17/2ddcab88-2d50-478a-9b2e-2c2978ad604b.html

Additional link:

http://tldp.org/HOWTO/Program-Library-HOWTO/dl-libraries.html

 

具體實現

dlopen

基本定義

功能:打開一個動態鏈接庫 
  包含頭文件: 
  #include  
  函數定義: 
  void * dlopen( const char * pathname, int mode ); 
  函數描述: 
  在dlopen的()函數以指定模式打開指定的動態連接庫文件,並返回一個句柄給調用進程。使用dlclose()來卸載打開的庫。 
  mode:分為這兩種 
  RTLD_LAZY 暫緩決定,等有需要時再解出符號 
  RTLD_NOW 立即決定,返回前解除所有未決定的符號。 
  RTLD_LOCAL 
  RTLD_GLOBAL 允許導出符號 
  RTLD_GROUP
  RTLD_WORLD 
  返回值: 
  打開錯誤返回NULL 
  成功,返回庫引用 
  編譯時候要加入 -ldl (指定dl庫) 
  例如 
  gcc test.c -o test -ldl
編輯本段
使用 dlopen
  dlopen()是一個強大的庫函數。該函數將打開一個新庫,並把它裝入內存。該函數主要用來加載庫中的符號,這些符號在編譯的時候是不知道的。比如 Apache Web 服務器利用這個函數在運行過程中加載模塊,這為它提供了額外的能力。一個配置文件控制了加載模塊的過程。這種機制使得在系統中添加或者刪除一個模塊時,都不需要重新編譯了。 
  可以在自己的程序中使用 dlopen()。dlopen() 在 dlfcn.h 中定義,並在 dl 庫中實現。它需要兩個參數:一個文件名和一個標志。文件名可以是我們學習過的庫中的 soname。標志指明是否立刻計算庫的依賴性。如果設置為 RTLD_NOW 的話,則立刻計算;如果設置的是 RTLD_LAZY,則在需要的時候才計算。另外,可以指定 RTLD_GLOBAL,它使得那些在以后才加載的庫可以獲得其中的符號。

  當庫被裝入后,可以把 dlopen() 返回的句柄作為給 dlsym() 的第一個參數,以獲得符號在庫中的地址。使用這個地址,就可以獲得庫中特定函數的指針,並且調用裝載庫中的相應函數。

 

dlsym
  dlsym()的函數原型是 
  void* dlsym(void* handle,const char* symbol) 
  該函數在文件中。 
  handle是由dlopen打開動態鏈接庫后返回的指針,symbol就是要求獲取的函數的名稱,函數返回值是void*,指向函數的地址,供調用使用

取動態對象地址:
#include
void *dlsym(void *pHandle, char *symbol);
dlsym根據動態鏈接庫操作句柄(pHandle)與符號(symbol),返回符號對應的地址。
使用這個函數不但可以獲取函數地址,也可以獲取變量地址。比如,假設在so中
定義了一個void mytest()函數,那在使用so時先聲明一個函數指針:
void (*pMytest)(),然后使用dlsym函數將函數指針pMytest指向mytest函數,
pMytest = (void (*)())dlsym(pHandle, "mytest");

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM