• dlopen、dlsym和dlclose的使用


    在dlopen()函数以指定模式打开指定的动态链接库文件,并返回一个句柄给dlsym()的调用进程。使用dlclose()来卸载打开的库。

    dlopen:

    dlopen()
    The function dlopen() loads the dynamic library file named by the null-terminated string filename and returns an opaque "handle" for the dynamic
    library. If filename is NULL, then the returned handle is for the main program. If filename contains a slash ("/"), then it is interpreted as a
    (relative or absolute) pathname. Otherwise, the dynamic linker searches for the library as follows (see ld.so(8) for further details)

    dlopen:功能:打开一个动态链接库 
      包含头文件: 
      #include <dlfcn.h> 
      函数定义: 
      void * dlopen( const char * pathname, int mode ); 

    编译时候要加入 -ldl (指定dl库)

    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标准。
     
    dlsym:
     void*dlsym(void* handle,const char* symbol)
    函数描述:
    dlsym根据动态链接库操作句柄(handle)与符号(symbol),返回符号对应的地址。使用这个函数不但可以获取函数地址,也可以获取变量地址
    handle是由dlopen打开动态链接库后返回的指针,symbol就是要求获取的函数或全局变量的名称。
    返回值:void* 指向函数的地址,供调用使用。

    dlclose()dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。

    dlerror()当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。

    示例:

    #include <stdio.h>
    void hello(void)
    {
        printf("hello ");
    }

    编译命令:

    gcc -shared -o hello.so hello.c
    代码#include <stdio.h>
    #include <stdlib.h>
    #include <dlfcn.h>
    
    int main(int argc, char **argv)
    {
        void *handle;
        void (*callfun)();
        char *error;
        handle = dlopen("/root/tmp/hello.so",RTLD_LAZY);  //如果hello.so不是在LD_LIBRARY_PATH所申明
                                                          //的路径中必须使用全路径名
        if(!handle)
        {
            printf("%s 
    ",dlerror());
            exit(1);
        }
        dlerror();
        callfun=dlsym(handle,"hello");
        if((error=dlerror())!=NULL)
        {
            printf("%s 
    ",error);
            exit(1);
        }
        callfun();
        dlclose(handle);
        return 0;
    }

    编译命令:

     gcc -o hello_dlopen hello_dlopen.c -ldl

    执行:./hello_dlopen

    从中可以体会到编译时不需要库的好处。另外一种在编译的时候需要动态库的使用方法:

    http://www.cnblogs.com/leaven/archive/2010/06/11/1756294.html

     示例2:

    hello.c函数原型:
      

    #include <sys/types.h>
    #include <signal.h>
    #include <stdio.h>
    #include <unistd.h>

    typedef struct {
     const char *module;
     int  (*GetValue)(char *pszVal);
     int   (*PrintfHello)();
    } hello_ST_API;


    int GetValue(char *pszVal)
    {
     int retval = -1;
     
     if (pszVal)
      retval = sprintf(pszVal, "%s", "123456");
      printf("%s, %d, pszVer = %s ", __FUNCTION__, __LINE__, pszVal);
     return retval;
    }

    int PrintfHello()
    {
     int retval = -1;
     
     printf("%s, %d, hello everyone ", __FUNCTION__, __LINE__);
     return 0;
    }

    const hello_ST_API  Hello = {
         .module = "hello",
       GetValue,
       PrintfHello,
    };

    编译的时候用指令:

    gcc -shared -o hello.so hello.c

    上面的函数是用一个全局结构体hello来指向。在dlsym定义中说不仅可以获取函数的地址,还可以获取全局变量的地址。所以此处是想通过dlsym来获取全局变量的地址。好处自己慢慢体会。

    3、dlopen代码

    #include <sys/types.h>
    #include <signal.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <dlfcn.h>

    typedef struct {
     const char *module;
     int  (*GetValue)(char *pszVal);
     int   (*PrintfHello)();
    } hello_ST_API;


    int main(int argc, char **argv)
    {
     hello_ST_API *hello;
     int i = 0;
     void *handle;
     char psValue[20] = {0};
     
     handle = dlopen(“库存放的绝对路径,你可以试试相对路径是不行的", RTLD_LAZY);
     if (! handle) {
      printf("%s,%d, NULL == handle ", __FUNCTION__, __LINE__);
      return -1;
     }
     dlerror();

     hello = dlsym(handle, "Hello");
     if (!hello) {
      printf("%s,%d, NULL == handle ", __FUNCTION__, __LINE__);
      return -1;
     }

     if (hello && hello->PrintfHello)
      i = hello->PrintfHello();
      printf("%s, %d, i = %d ", __FUNCTION__, __LINE__, i);
     if (hello && hello->GetValue)
      i = hello->GetValue(psValue);

     if (hello && hello->module)
      {
       printf("%s, %d, module = %s ", __FUNCTION__, __LINE__, hello->module);
      }

        dlclose(handle);
        return 0;
    }

    编译指令:gcc -o test hello_dlopen.c -ldl

    运行./test结果如下。

    PrintfHello, 27, hello everyone
    main, 36, i = 0
    GetValue, 19, pszVer = 123456
    main, 42, module = hello

    可以看到结果正常出来了。

    看到没用?dlsym找到全局结构体hello后,可以直接用这个全局结构体指针来使用库里面的函数了,因为我们有时候提供的库不仅仅是一个两个函数的,一般的一个库都会存在多个函数,用这种方式就可以直接使用了。不然找函数名称的话要写多少个dlsym啊?

     

    参考:

    http://www.cnblogs.com/leaven/archive/2011/01/28/1947180.html

     

     
  • 相关阅读:
    Java中Runnable和Thread的区别
    Callable,Runnable比较及用法
    如何实现视差滚动效果的网页?
    【175】Easy CHM的使用
    【174】C#添加非默认字体
    【173】双显示器随便切换位置
    【172】outlook邮箱设置
    【171】IDL读取HDF文件
    怎样实现二级联动
    Java 23种设计模式详尽分析与实例解析之二--结构型模式
  • 原文地址:https://www.cnblogs.com/youxin/p/5109520.html
Copyright © 2020-2023  润新知