dlopen 方式調用 Linux 的動態鏈接庫


在dlopen()函數以指定模式打開指定的動態鏈接庫文件。並返回一個句柄給 dlsym()的調用進程。

使用 dlclose()來卸載打開的庫。

功能:打開一個動態鏈接庫,並返回動態鏈接庫的句柄
包括頭文件:
#include <dlfcn.h>
函數定義:
void * dlopen( const char * pathname, int mode);
函數描寫敘述:
mode是打開方式,其值有多個,不同操作系統上實現的功能有所不同,在linux下,按功能可分為三類:
1、解析方式
RTLD_LAZY:在dlopen返回前,對於動態庫中的沒有定義的符號不運行解析(僅僅對函數引用有效。對於變量引用總是馬上解析)。

RTLD_NOW: 須要在dlopen返回前。解析出全部沒有定義符號,假設解析不出來。在dlopen會返回NULL,錯誤為:: undefined symbol: xxxx....... 2、作用范圍,可與解析方式通過“|”組合使用。

RTLD_GLOBAL:動態庫中定義的符號可被其后打開的其他庫解析。 RTLD_LOCAL: 與RTLD_GLOBAL作用相反,動態庫中定義的符號不能被其后打開的其他庫重定位。

假設沒有指明是RTLD_GLOBAL還是RTLD_LOCAL。則缺省為RTLD_LOCAL。 3、作用方式 RTLD_NODELETE: 在dlclose()期間不卸載庫,而且在以后使用dlopen()又一次載入庫時不初始化庫中的靜態變量。這個flag不是POSIX-2001標准。 RTLD_NOLOAD: 不載入庫。

可用於測試庫是否已載入(dlopen()返回NULL說明未載入,否則說明已載入),也可用於改變已載入庫的flag,如:先前載入庫的flag為RTLD_LOCAL,用dlopen(RTLD_NOLOAD|RTLD_GLOBAL)后flag將變成RTLD_GLOBAL。這個flag不是POSIX-2001標准。 RTLD_DEEPBIND:在搜索全局符號前先搜索庫內的符號。避免同名符號的沖突。這個flag不是POSIX-2001標准。 返回值: 打開錯誤返回NULL 成功,返回庫引用 編譯時候要增加 -ldl (指定dl庫) 比如 gcc test.c -o test -ldl



#include <stdlib.h>
#include <dlfcn.h>
#include <stdio.h>

//申明結構體
typedef struct __test {
    int i;
    void (* echo_fun)(struct __test *p);
}Test;

//供動態庫使用的注冊函數
void __register(Test *p) {
    p->i = 1;
    p->echo_fun(p);
}

int main(void) {

    void *handle = NULL;
    char *myso = "./mylib.so";

    if((handle = dlopen(myso, RTLD_NOW)) == NULL) {
        printf("dlopen - %sn", dlerror());
        exit(-1);
    }

    return 0;
}

#include <stdio.h>
#include <stdlib.h>

//申明結構體類型
typedef struct __test {
    int i;
    void (*echo_fun)(struct __test *p);
}Test;

//申明注冊函數原型
void __register(Test *p);

static void __printf(Test *p) {
    printf("i = %dn", p->i);
}

//動態庫申請一個全局變量空間
//這樣的 ".成員"的賦值方式為c99標准
static Test config = {
    .i = 0,
    .echo_fun = __printf,
};

//載入動態庫的自己主動初始化函數
void _init(void) {
    printf("initn");
    //調用主程序的注冊函數
    __register(&config);
}


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

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

主程序通過dlopen()載入一個.so的動態庫文件, 然后動態庫會自己主動執行 _init() 初始化函數, 初始化函數打印一個提示信息, 然后調用主程序的注冊函數給結構體又一次賦值, 然后調用結構體的函數指針, 打印該結構體的值. 這樣就充分的達到了主程序和動態庫的函數相互調用和指針的相互傳遞.

gcc參數 -rdynamic 用來通知鏈接器將全部符號加入到動態符號表中(目的是可以通過使用 dlopen 來實現向后跟蹤).

gcc參數 -fPIC 作用: 當使用.so等類的庫時,當遇到多個可運行文件共用這一個庫時, 在內存中,這個庫就不會被復制多份,讓每一個可運行文件一對一的使用,而是讓多個可運行文件指向一個庫文件,達到共用. 宗旨:節省了內存空間,提高了空間利用率.

<pre name="code" class="cpp">dlsym函數:
  函數原型是
  void* dlsym(void* handle,const char* symbol)
  該函數在<dlfcn.h>文件里。

handle是由dlopen打開動態鏈接庫后返回的指針。symbol就是要求獲取的函數的名稱,函數 返回值是void*,指向函數的地址,供調用使用。



 
        
導入庫函數使用方法:
 

#include <dlfcn.h>
void* handle = dlopen("./hello.so", RTLD_LAZY);
typedef void (*hello_t)();
hello_t hello = (hello_t) dlsym(handle, "hello");

hello();
dlclose(handle);

注意庫函數在庫中的定義要用extern“c”來申明,這樣在主函數中才干通過“hello”來查找函數。申明的方式有下面兩種:

extern "C" int foo;
extern "C" void bar();
            
and 

extern "C" {
     extern int foo;
     extern void bar();
}

導入類庫方法:

#include "polygon.hpp" //類定義處

#include <dlfcn.h>

void* triangle = dlopen("./triangle.so", RTLD_LAZY);
create_t* create_triangle = (create_t*) dlsym(triangle, "create");

destroy_t* destroy_triangle = (destroy_t*) dlsym(triangle, "destroy");
polygon* poly = create_triangle();
// use the class

    poly->set_side_length(7);
    cout << "The area is: " << poly->area() << '\n';
// destroy the class

    destroy_triangle(poly);

    // unload the triangle library

    dlclose(triangle);




免責聲明!

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



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