• 动态链接库的显示调用


    动态链接库的显示调用

    上篇文章介绍了动态链接库的创建和试用,需要指明的是,这只是动态链接库的其中一种用法。本文将讲解它的另外一种用法,总的来讲,动态链接库的调用方式有 2 种,分别是:

    • 隐式调用(静态调用):将动态链接库和其它源程序文件(或者目标文件)一起参与链接;
    • 显式调用(动态调用):手动调用动态链接库中包含的资源,同时用完后要手动将资源释放。

    显式调用动态链接库的过程,类似于使用 malloc() 和 free()(C++ 中使用 new 和 delete)管理动态内存空间,需要时就申请,不需要时就将占用的资源释放。由此可见,显式调用动态链接库对内存的使用更加合理。常用于一些大型项目中。

    显示调用过程

    和隐式调用动态链接库不同,在 C/C++ 程序中显示调用动态链接库时,无需引入和动态链接库相关的头文件。但与此同时,程序中需要引入另一个头文件,即 <dlfcn.h> 头文件,因为要显式调用动态链接库,需要使用该头文件提供的一些函数。

    #include <dlfcn.h>

    该头文件中,以下几个函数是显式调用动态链接库时常用的:
    1) 类似于读写文件前必须先打开文件,要想显示调用某个动态链接库提供的资源,首先要做的就是打开该库文件。打开库文件,其本质就是将库文件装载到内存中,为后续使用做准备。打开动态库文件,需要借助 dlopen() 函数,其语法格式为:

    void *dlopen (const char *filename, int flag);

    其中,filename 参数用于表明目标库文件的存储位置和库名;flag 参数的值有 2 种:

    1. RTLD_NOW:将库文件中所有的资源都载入内存;
    2. RTLD_LAZY:暂时不将库文件中的资源载入内存,使用时才载入。

    注意:对于 filename 参数,如果用户提供的是以 / 开头,即以绝对路径表示的文件名,则函数会前往该路径下查找库文件;反之,如果用户仅提供文件名,则该函数会依次前往 LD_LIBRARY_PATH 环境变量指定的目录、/etc/ld.so.cache 文件中指定的目录、/usr/lib、/usr/lib64、/lib、/lib64 等默认搜索路径中查找。

    2) 借助 dlsym() 函数可以获得指定函数在内存中的位置,其语法格式为:

    void *dlsym(void *handle, char *symbol);

    其中,hanle 参数表示指向已打开库文件的指针;symbol 参数用于指定目标函数的函数名。
    如果 dlsym() 函数成功找到指定函数,会返回一个指向该函数的指针;反之如果查找失败,函数会返回 NULL。
    3) 和 dlopen() 相对地,借助 dlclose() 函数可以关闭已打开的动态链接库。该函数的语法格式如下:

    int dlclose (void *handle);

    其中,handle 表示已打开的库文件指针。当函数返回 0 时,表示函数操作成功;反之,函数执行失败。

    注意,调用 dlclose() 函数并不一定会将目标库彻底释放,它只会是目标库的引用计数减 1,当引用计数减为 0 时,库文件所占用的资源才会被彻底释放

    4) 借助 dlerror() 函数,我们可以获得最近一次 dlopen()、dlsym() 或者 dlclose() 函数操作失败的错误信息。该函数的语法格式如下:

    const char *dlerror(void);

    可以看到,该函数不需要传递任何参数。同时,如果函数返回 NULL,则表明最近一次操作执行成功。

    示例:

    这里通过C 语言项目实例,演示显式调用动态链接库的具体实现过程。这里仍引用前面章节中创建的 项目,其项目结构如下:

    [root@bogon demo]# ls                       <- demo 目录结构
    add.c  div.c  main.c  sub.c  test.h
    [root@bogon demo]# cat test.h           <- test.h 文件内容
    #ifndef __TEST_H_
    #define __TEST_H_
    
    int add(int a,int b);
    int sub(int a,int b);
    int div(int a,int b);
    
    #endif
    [root@bogon demo]# cat add.c           <- add.c 文件内容
    #include "test.h"
    int add(int a,int b)
    {
        return a + b;
    }
    [root@bogon demo]# cat sub.c           <- sub.c 文件内容
    #include "test.h"
    int sub(int a,int b)
    {
        return a - b;
    }
    [root@bogon demo]# cat div.c           <- div.c 文件内容       
    #include "test.h"
    int div(int a,int b)
    {
        return a / b;
    }
    [root@bogon demo]# cat main.c        <- main.c 文件内容
    #include <stdio.h>
    #include "test.h"  //必须引入头文件
    int main(void)
    {
        int m, n;
        printf("Input two numbers: ");
        scanf("%d %d", &m, &n);
        printf("%d+%d=%d
    ", m, n, add(m, n));
        printf("%d-%d=%d
    ", m, n, sub(m, n));
        printf("%d÷%d=%d
    ", m, n, div(m, n));
        return 0;
    }

    对于 add.c、sub.c 和 div.c 这 3 个源文件,我们可以将它们打包生成一个动态链接库:

     接下来重点分析 main.c 主程序文件的代码:

    #include <stdio.h>
    #include <dlfcn.h>
    int main()
    {
        int m,n;
        //打开库文件
        void* handler = dlopen("libmymath.so",RTLD_LAZY);
        if(dlerror() != NULL){
            printf("%s",dlerror());
        }
       
        //获取库文件中的 add() 函数
        int(*add)(int,int)=dlsym(handler,"add");
        if(dlerror()!=NULL){
            printf("%s",dlerror());
        }
      
        //获取库文件中的 sub() 函数
        int(*sub)(int,int)=dlsym(handler,"sub");
        if(dlerror()!=NULL){
            printf("%s",dlerror());
        }
    
        //获取库文件中的 div() 函数
        int(*div)(int,int)=dlsym(handler,"div");
        if(dlerror()!=NULL){
            printf("%s",dlerror());
        }
        //使用库文件中的函数实现相关功能
        printf("Input two numbers: ");
        scanf("%d %d", &m, &n);
        printf("%d+%d=%d
    ", m, n, add(m, n));
        printf("%d-%d=%d
    ", m, n, sub(m, n));
        printf("%d÷%d=%d
    ", m, n, div(m, n));
        //关闭库文件
        dlclose(handler); 
        return 0;
    }

    首先,该程序中并没有引入 test.h 头文件,因为对于显式调用动态链接库来说,并不需要引入它。与此同时,在使用库文件中的相关函数之前,我们需要先调用 dlopen() 函数打开库文件,然后才能通过 dlsym() 函数找到相关的函数。另外,最后不要忘记调用 dlclose() 函数关闭库文件。

    通过执行如下指令,即可生成相应的可执行文件:

     

     这里需要添加 -ldl 选项(该可执行程序需要 libdl.so 动态库的支持)。运行 main.exe

     

  • 相关阅读:
    Windows 服务多语言化时读取配置文件失败的问题。
    从开始界面卸载Windows服务时,不小心点击了 取消,此后再次卸载会卸载不掉
    从数据库导出数据时,有的字段是时间,不同的时间向在窗口中去掉时用正则表达式匹配找到不同的时间
    C#使用ManagementObjectSearcher来获取系统信息时会有out of memory的异常
    C# TreeView设置SelectedNode设置无效的问题
    C#判断ListBox是否显示了水平滚动条/横向滚动条
    Jmeter运行不显示cmd对话框
    监控Linux系统所选的服务所占进程内存占用
    Linux的date用法
    shell的循环嵌套语法
  • 原文地址:https://www.cnblogs.com/jkin/p/13740545.html
Copyright © 2020-2023  润新知