dlopen函數詳解

來源:互聯網
上載者:User

標籤:

Linux提供了一套API來動態裝載庫。下面列出了這些API:

- dlopen,開啟一個庫,並為使用該庫做些準備。
- dlsym,在開啟的庫中尋找符號的值。
- dlclose,關閉庫。
- dlerror,返回一個描述最後一次調用dlopen、dlsym,或dlclose的錯誤資訊的字串。

C語言使用者需要包含標頭檔dlfcn.h才能使用上述API。glibc還增加了兩個POSIX標準中沒有的API:
- dladdr,從函數指標解析符號名稱和所在的檔案。
- dlvsym,與dlsym類似,只是多了一個版本字串參數。

在Linux上,使用動態連結的應用程式需要和庫libdl.so一起連結,也就是使用選項-ldl。但是,編譯時間不需要和動態裝載的庫一起連結。程式3-1是一個在Linux上使用dl*常式的簡單樣本。



延遲重定位(Lazy Relocation)
延遲重定位/裝載是一個允許符號只在需要時才重定位的特性。這常在各UNIX系統上解析函數調用時用到。當一個和共用庫一起連結的應用程式幾乎不會用到該共用庫中的函數時,該特性被證明是非常有用的。這種情況下,只有庫中的函數被應用程式調用時,共用庫才會被裝載,否則不會裝載,因此會節約一些系統資源。但是如果把環境變數LD_BIND_NOW設定成一個非空值,所有的重定位操作都會在程式啟動時進行。也可以在連結器命令列通過使用-z now連結器選項使延遲綁定對某個特定的共用庫失效。需要注意的是,除非重新連結該共用庫,否則對該共用庫的這種設定會一直有效。


初始化(initializing)和終止化(finalizing)函數
有時候,以前的代碼可能用到了兩個特殊的函數:_init和_fini。_init和_fini函數用在裝載和卸載某個模組(注釋14)時分別控制該模組的構造器和析構器(或建構函式和解構函式)。他們的C語言原型如下:
void _init(void);
void _fini(void);
當一個庫通過dlopen()動態開啟或以共用庫的形式開啟時,如果_init在該庫中存在且被輸出出來,則_init函數會被調用。如果一個庫通過dlclose()動態關閉或因為沒有應用程式引用其符號而被卸載時,_fini函數會在庫卸載前被調用。當使用你自己的_init和_fini函數時,需要注意不要與系統開機檔案一起連結。可以使用GCC選項 -nostartfiles 做到這一點。
但是,使用上面的函數或GCC的-nostartfiles選項並不是很好的習慣,因為這可能會產生一些意外的結果。相反,庫應該使用__attribute__((constructor))和__attribute__((destructor))函數屬性來輸出它的建構函式和解構函式。如下所示:
void __attribute__((constructor)) x_init(void)
void __attribute__((destructor)) x_fini(void)
建構函式會在dlopen()返回前或庫被裝載時調用。解構函式會在這樣幾種情況下被調用:dlclose()返回前,或main()返回後,或裝載庫過程中exit()被調用時。

 

我們通過一個例子來講解dlopen系列函數的使用和操作:

主程式:

  1. #include <stdlib.h>
  2. #include <dlfcn.h>
  3. #include <stdio.h>
  4. //申明結構體
  5. typedef struct __test {
  6.     int i;
  7.     void (* echo_fun)(struct __test *p);
  8. }Test;
  9. //供動態庫使用的註冊函數
  10. void __register(Test *p) {
  11.     p->i = 1;
  12.     p->echo_fun(p);
  13. }
  14. int main(void) {
  15.     void *handle = NULL;
  16.     char *myso = "./mylib.so";
  17.     if((handle = dlopen(myso, RTLD_NOW)) == NULL) {
  18.         printf("dlopen - %sn", dlerror());
  19.         exit(-1);
  20.     }
  21.     return 0;
  22. }

 

動態庫:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. //申明結構體類型
  4. typedef struct __test {
  5.     int i;
  6.     void (*echo_fun)(struct __test *p);
  7. }Test;
  8. //申明註冊函數原型
  9. void __register(Test *p);
  10. static void __printf(Test *p) {
  11.     printf("i = %dn", p->i);
  12. }
  13. //動態庫申請一個全域變數空間
  14. //這種 ".成員"的賦值方式為c99標準
  15. static Test config = {
  16.     .i = 0,
  17.     .echo_fun = __printf,
  18. };
  19. //載入動態庫的自動初始化函數
  20. void _init(void) {
  21.     printf("initn");
  22.     //調用主程式的註冊函數
  23.     __register(&config);
  24. }

主程式編譯: gcc test.c -ldl -rdynamic

動態庫編譯: gcc -shared -fPIC -nostartfiles -o mylib.so mylib.c

主程式通過dlopen()載入一個.so的動態庫檔案, 然後動態庫會自動運行 _init() 初始化函數, 初始化函數列印一個提示資訊, 然後調用主程式的註冊函數給結構體重新賦值, 然後調用結構體的函數指標, 列印該結構體的值. 這樣就充分的達到了主程式和動態庫的函數相互調用和指標的相互傳遞.

gcc參數 -rdynamic 用來通知連結器將所有符號添加到動態符號表中(目的是能夠通過使用 dlopen 來實現向後跟蹤).

gcc參數 -fPIC 作用: 當使用.so等類的庫時,當遇到多個可執行檔共用這一個庫時, 在記憶體中,這個庫就不會被複製多份,讓每個可執行檔一對一的使用,而是讓多個可執行檔指向一個庫檔案,達到共用. 宗旨:節省了記憶體空間,提高了空間利用率.

dlopen函數詳解

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.