IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> dlopen()实现三方库的动态加载 -> 正文阅读

[C++知识库]dlopen()实现三方库的动态加载

目录

一、函数介绍

?二、实现热更新


一、函数介绍

动态加载也就是运行时加载,即可以在程序运行时由我们决定何时加载指定的模块。这样进程启动时只加载必要的模块就行,减少了内存占用,除此之外最大的优点是,可以实现在不重启程序的情况下,实现模块的重新加载。这种技术也叫做“热更新”。

先看一下函数原型和功能:

// 按指定的模式打开动态链接库文件,并返回句柄
void *dlopen(const char *filename, int flags);
// 通过句柄获取共享对象或可执行文件中符号的地址
void *dlsym(void *handle, const char *symbol);
// 卸载打开的库
int dlclose(void *handle);

简单的例子实现一下dlopen的动态加载功能:

libcaculator.so动态库的主要方法如下:

    int add(int a,int b)
? ? {
? ? ? ? return (a + b);
? ? }
? ? int sub(int a, int b)
? ? {
? ? ? ? return (a - b);
? ? }
? ? int mul(int a, int b)
? ? {
? ? ? ? return (a * b);
? ? }
? ? int div(int a, int b)
? ? {
? ? ? ? return (a / b);
? ? }

编译打包命令:

gcc -fPIC -shared caculatoe.cc -o libcaculator.so

然后在demo.cc中使用dlopen打开动态库并调用相关函数

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
//动态链接库路径
#define LIB_CACULATE_PATH "./libcaculator.so"
//函数指针
typedef int (*CAC_FUNC)(int, int);
int main()
{
? ? void *handle;
? ? char *error;
? ? CAC_FUNC cac_func = NULL;
? ? //打开动态链接库
? ? handle = dlopen(LIB_CACULATE_PATH, RTLD_LAZY);
? ? if (!handle) {
? ? fprintf(stderr, "%s\n", dlerror());
? ? exit(EXIT_FAILURE);
? ? }
? ? //清除之前存在的错误
? ? dlerror();
? ? //获取一个函数
? ? *(void **) (&cac_func) = dlsym(handle, "add");
? ? if ((error = dlerror()) != NULL) ?{
? ?     fprintf(stderr, "%s\n", error);
? ?     exit(EXIT_FAILURE);
? ? }
? ? printf("add: %d\n", (*cac_func)(2,7));
? ? cac_func = (CAC_FUNC)dlsym(handle, "sub");
? ? printf("sub: %d\n", cac_func(9,2));
? ? cac_func = (CAC_FUNC)dlsym(handle, "mul");
? ? printf("mul: %d\n", cac_func(3,2));
? ? cac_func = (CAC_FUNC)dlsym(handle, "div");
? ? printf("div: %d\n", cac_func(8,2));
? ? //关闭动态链接库
? ? dlclose(handle);
? ? exit(EXIT_SUCCESS);
}

编译命令:

gcc demo.cc -o demo -ldl

需要链接dl库

编译报错:

./libcaculator.so: undefined symbol: add

?用nm查看libcaculator.so生成的符号如下:

0000000000004020 b completed.8060
                 w __cxa_finalize
0000000000001040 t deregister_tm_clones
00000000000010b0 t __do_global_dtors_aux
0000000000003e88 d __do_global_dtors_aux_fini_array_entry
0000000000004018 d __dso_handle
0000000000003e90 d _DYNAMIC
0000000000001158 t _fini
00000000000010f0 t frame_dummy
0000000000003e80 d __frame_dummy_init_array_entry
0000000000002118 r __FRAME_END__
0000000000004000 d _GLOBAL_OFFSET_TABLE_
                 w __gmon_start__
0000000000002000 r __GNU_EH_FRAME_HDR
0000000000001000 t _init
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
0000000000001070 t register_tm_clones
0000000000004020 d __TMC_END__
00000000000010f9 T _Z3addii
000000000000113e T _Z3divii
0000000000001127 T _Z3mulii
0000000000001111 T _Z3subii

可以看到add方法被编译器重命名为了_Z3addii,这是因为C++代码在编译时,编译器会进行特殊处理,为了支持函数重载,会对函数进行重命名。

解决方法是使用extern C,让编译器以C语言的方式处理代码。

修改caculator.cc文件如下:

#ifdef __cplusplus
extern "C" {
#endif
? ? int add(int a,int b)
? ? {
? ? ? ? return (a + b);
? ? }
? ? int sub(int a, int b)
? ? {
? ? ? ? return (a - b);
? ? }
? ? int mul(int a, int b)
? ? {
? ? ? ? return (a * b);
? ? }
? ? int dv(int a, int b)
? ? {
? ? ? ? return (a / b);
? ? }
#ifdef __cplusplus
}
#endif

然后重新编译运行demo即可

?二、实现热更新

重新修改一下文件实现

demo.h代码如下:

#ifndef DEMO_H
#define DEMO_H
#include <stdio.h>
// #include <stdlib.h>
#include <dlfcn.h>

//动态链接库路径
#define LIB_CACULATE_PATH "./libcaculator.so"

//函数指针
typedef int (*CAC_FUNC)(int, int);

void test(int x);

#endif

demo.cc如下:

#include "demo.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <time.h>
#include <thread>
#include <atomic>
void *handle;
std::thread th;
std::atomic<int> flag(0);
std::atomic<bool> start(false);
int hash_load = 0;
void test (int x) {
    printf("%d\n", x);
}

void load() {
    
    char *error;
    CAC_FUNC cac_func = NULL;
    
    struct stat attr;
    time_t start_time;
    while(start) {
        
        if (stat(LIB_CACULATE_PATH, &attr) == 0 && attr.st_ino != -1) {
            if (attr.st_mtim.tv_sec != start_time && flag == 1) {
                printf("start time: %ld, last_time:%ld\n", start_time, attr.st_mtim.tv_sec);
                printf("need to close first\n");
                start_time = attr.st_mtim.tv_sec;
                flag = 0;
                dlclose(handle);
            }

            if (!flag) {
                printf("need to open\n");
                
                //打开动态链接库
                handle = dlopen(LIB_CACULATE_PATH, RTLD_LAZY);
                if (!handle) {
                    // fprintf(stderr, "%s\n", dlerror());
                    printf("%s\n", dlerror());
                    continue;
                }
                //清除之前存在的错误
                start_time = attr.st_mtim.tv_sec;
                dlerror();
                flag = 1;
                //获取一个函数
                *(void **) (&cac_func) = dlsym(handle, "add");
                if ((error = dlerror()) != NULL)  {
                    printf("%s\n", error);
                    continue;
                }
                printf("add: %d\n", (*cac_func)(2,7));
            }
        }
        sleep(1);
    }

    return ;
}

void toload() {
    start = true;
    th = std::thread(load);
}
void tounload() {
    start = false;
    th.join();
    printf("unload \n");
    
    
    dlclose(handle);
}
int main()
{
    toload();
    sleep(10);
    tounload();
    return 0;
}

这时候重新运行demo的可执行文件,然后修改后重新编译libcaculator.so即可实现libcaculator.so的热更新,运行结果如下:

?

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-11-19 17:27:06  更:2021-11-19 17:29:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:28:57-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码