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);



posted @ 2017-06-13 09:37  brucemengbm  阅读(20990)  评论(0编辑  收藏  举报