dlopen 方式调用 Linux 的动态链接库

来源:互联网 发布:sql合并相同行 编辑:程序博客网 时间:2024/06/05 11:58

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



1 0