Linux下c语言实现动态库的动态调用

news2024/9/24 15:17:06

在Linux操作系统下,有时候需要在不重新编译程序的情况下,运行时动态地加载库,这时可以通过Linux操作系统提供的API可以实现,涉及到的API主要有dlopen、dlsym和dlclose。使用时,需要加上头文件#include <dlfcn.h> 。

dlopen介绍:打开一个动态链接库 ,函数定义如下:

void * dlopen( const char * pathname, int mode ); 
函数功能描述:在dlopen的()函数以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程。

参数说明:

pathname:动态库的名称,需要带上路径。
mode:分为这几种 
RTLD_LAZY 暂缓决定,等有需要时再解出符号 
RTLD_NOW 立即决定,返回前解除所有未决定的符号。 
RTLD_LOCAL 
RTLD_GLOBAL 允许导出符号 
RTLD_GROUP 
RTLD_WORLD 
返回值说明: 
打开错误返回空指针NULL ,若成功,返回库引用 

dlsym介绍:

该函数根据动态链接库操作句柄与符号,返回符号对应的地址。

函数定义如下:

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

函数说明:

dlsym根据动态链接库操作句柄和符号,返回符号对应的地址。使用这个函数不但可以获取函数地址,也可以获取变量地址。

参数说明:

handle:打开库文件之后的句柄。

symbol:需要从库文件查找的符号。

dlclose

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

实验一 获取函数地址

在linux下创建一个test的工程目录。

  1. 工程目录下创建一个名为lib1.c的文件,写入如下内容:
    #include<stdio.h>
    #include<stdlib.h>
    #include <stdarg.h>
    
    void LOG(const char *format, ...)
    {
            va_list argptr;
            char buffer[2048];
            va_start(argptr,format);
            vsprintf(buffer,format,argptr);
            va_end(argptr);
    
            printf("%s\n", buffer);
    }
    
    void lib_function_1(void)
    {
    	LOG("call %s!!!", __func__);
    }
    
    void lib_function_2(void)
    {
    	LOG("call %s!!!", __func__);
    }
  2. 创建一个main.c文件,内容如下所示:
    #include<stdio.h>
    #include<stdlib.h>
    #include <dlfcn.h>
    
    void dynamic_lib_test()
    {
    	void (*fun)();
    
    	void *hander = NULL;
    
    	hander = dlopen("./libshare.so", RTLD_NOW);
    
    	if(hander == NULL) {
    		printf("can not find dlib\n");
    		return;
    	}
    
    	fun = (void(*)())dlsym(hander, "lib_function_1");
    	if(fun==NULL) {
    		printf("can't find function\n");
    	}
    	fun();
    
    	fun = (void(*)())dlsym(hander, "lib_function_2");
    	if(fun==NULL) {
    		printf("can't find function\n");
    	}
    	fun();
    
    	dlclose(hander);
    }
    
    int main(int argc, char *argv[])
    {
    	dynamic_lib_test();
    	return 0;
    }
  3. 创建一个Makefile,生成一个动态库以及可执行文件,内容如下所示:
    CPROG	= test
    BIN     = $(CPROG) 
    CC= gcc
    OBJS=main.o lib1.o
    
    LDFLAGS += -ldl
    
    all: $(BIN) 
    clean:
    	rm -f $(OBJS) $(BIN)
    $(BIN): $(OBJS)
    	$(CC) -g -fPIC -shared lib1.c -o libshare.so
    	$(CC)  -o $(BIN) $(OBJS) $(CFLAGS) $(LDFLAGS) $(CFLAGS_EXTRA)
  4. 编译该工程的代码

    执行命令:make clean;make

    最终会在该工程生成一个libshare.so动态库文件以及test的可执行文件。

  5. 测试验证在该工程下执行./test,便可以观察到最终结果,如下图所示:

实验二 获取全局变量地址

  1. 在当前目录下新增lib2.c,写入如下内容:
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<stdarg.h>
    
    //声明一个结构体
    typedef struct test_s {
    	char *test_name;
    	void (*test_func)();
    	void (*test_set_buf)(char *val);
    	char *(*test_get_buf)();
    }test_t;
    
    char testbuf[128]="123456";
    
    void Log(const char *format, ...)
    {
            va_list argptr;
            char buffer[2048];
            va_start(argptr,format);
            vsprintf(buffer,format,argptr);
            va_end(argptr);
    
            printf("%s\n", buffer);
    }
    void test_function()
    {
    	Log("function %s call!!!", __func__);
    }
    
    void test_set_buf(char *val)
    {
    	strcpy(testbuf, val);
    }
    
    char *test_get_buf()
    {
    	return testbuf;
    }
    //定义一个全局变量
    test_t test = {
    	.test_name = "TestName",
    	.test_func = test_function,
    	.test_get_buf = test_get_buf,
    	.test_set_buf = test_set_buf,
    };
  2. 编辑main.c,内容如下:
    #include <string.h>
    
    void dynamic_lib_test_1()
    {
    	void (*fun)();
    
    	void *hander = NULL;
    
    	hander = dlopen("./libshare.so", RTLD_NOW);
    
    	if(hander == NULL) {
    		printf("can not find dlib\n");
    		return;
    	}
    
    	fun = (void(*)())dlsym(hander, "lib_function_1");
    	if(fun==NULL) {
    		printf("can't find function\n");
    	}
    	fun();
    
    	fun = (void(*)())dlsym(hander, "lib_function_2");
    	if(fun==NULL) {
    		printf("can't find function\n");
    	}
    	fun();
    
    	dlclose(hander);
    }
    
    typedef struct test_s {
    	char *test_name;
    	void (*test_func)();
    	void (*test_set_buf)(char *val);
    	char *(*test_get_buf)();
    }test_t;
    
    void dynamic_lib_test_2()
    {
    	
    	void *hander = NULL;
    
    	hander = dlopen("./libshare.so", RTLD_NOW);
    
    	if(hander == NULL) {
    		printf("can not find dlib\n");
    		return;
    	}
    
    	test_t *t = (test_t *)dlsym(hander, "test");
    	if(t==NULL) {
    		printf("can't find function\n");
    		return;
    	}
    
    	printf("name:%s, buf:%s\n", t->test_name, t->test_get_buf());
    	t->test_func();
    	t->test_set_buf("hello world!!!");
    	printf("name:%s, buf:%s\n", t->test_name, t->test_get_buf());
    
    	dlclose(hander);
    }
    
    int main(int argc, char *argv[])
    {
    	dynamic_lib_test_1();
    	dynamic_lib_test_2();
    	return 0;
    }
    
  3. 编辑Makefile,主要是添加lib2.c的编译
    CPROG	= test
    BIN     = $(CPROG) 
    CC= gcc
    OBJS=main.o lib1.o
    LDFLAGS += -ldl
    
    all: $(BIN) 
    clean:
    	rm -f $(OBJS) $(BIN)
    $(BIN): $(OBJS)
    	$(CC) -g -fPIC -shared lib1.c lib2.c -o libshare.so
    	$(CC)  -o $(BIN) $(OBJS)   $(CFLAGS) $(LDFLAGS) $(CFLAGS_EXTRA) 

 4.执行make clean;make重新编译这个工程

5.测试验证

dlsym找到全局结构体test后,可以直接用这个全局结构体指针来使用库里面的函数了。 

总结:

通过dlopen打开动态库的方式,允许在运行时动态地加载库,这可以让你在不重新编译程序的情况下,添加或修改库中的函数,同时也为程序提供了更大的灵活性。dlsym允许程序在运行时查找库中的符号(通常是函数或变量的名称)。这使得程序可以在运行时决定调用哪个版本的函数,或者根据需要选择不同的实现。由于只有当程序实际需要时才加载库,因此可以节省内存。如果多个程序共享同一个库,那么这个库只需要在内存中加载一次。使用动态链接,你可以更容易地控制库的版本。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1324550.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux 操作系统(用户注册、删除、权限修改等)

添加用户 格式&#xff1a;useradd 用户名 ( 添加用户 ) passwd 用户名 (给用户设置密码) Linux 用户切换原则&#xff1a; 高权限向低权限切换无需输入密码 低权限向高权限或同级用户切换需要输入密码 用户切换&#xff1a;su su – 用户名 &#xff08;用户切换&#x…

Appium+Python自动化环境搭建实例教程

前言 appium可以说是做app最火的一个自动化框架&#xff0c;它的主要优势是支持android和ios&#xff0c;另外脚本语言也是支持java和Python。 小编擅长Python&#xff0c;所以接下来的教程是appiumpython的实例。 学习appium最大的难处在于环境的安装&#xff0c;从入门到真…

智能优化算法应用:基于混沌博弈算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于混沌博弈算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于混沌博弈算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.混沌博弈算法4.实验参数设定5.算法结果6.…

K8s内容器拓扑图工具

1.背景&#xff1a;随着线上容器越来越多&#xff0c;需要一个可视化的方式展示各个容器之间的拓扑图。 2.需求&#xff1a;轻量级&#xff0c;部署方便。 3.部署 helm repo add groundcover https://helm.groundcover.com/ helm repo update helm install caretta --namespa…

springboot解决XSS存储型漏洞

springboot解决XSS存储型漏洞 XSS攻击 XSS 攻击&#xff1a;跨站脚本攻击(Cross Site Scripting)&#xff0c;为不和 前端层叠样式表(Cascading Style Sheets)CSS 混淆&#xff0c;故将跨站脚本攻击缩写为 XSS。 XSS(跨站脚本攻击)&#xff1a;是指恶意攻击者往 Web 页面里插…

2_js运算符与流程控制语句

1. 运算符的应用 1.1 算数运算符 浮点数的精度问题 浮点数值的最高精度是17位小数&#xff0c;不要直接判断两个浮点数是否相等。 var result 0.1 0.2; // 结果不是 0.3&#xff0c;而是&#xff1a;0.30000000000000004 console.log(0.07 * 100); // 结果不是 7&#…

CSS设计器的使用

目录 css的概念 css的优势 css的基本语法 html中引入css样式 CSS基本选择器 选择器的使用 初级选择器&#xff1a; 标签选择器 类选择器 id选择器 高级选择器(结构选择器&#xff09; ①后代选择器(E F) ②子选择器(E>F) ③相邻兄弟选择器(EF) ④通用兄弟选择器(…

【halcon深度学习之那些封装好的库函数】determine_dl_model_detection_param

determine_dl_model_detection_param 目标检测的数据准备过程中的有一个库函数determine_dl_model_detection_param “determine_dl_model_detection_param” 直译为 “确定深度学习模型检测参数”。 这个过程会自动针对给定数据集估算模型的某些高级参数&#xff0c;强烈建议…

23 聪明的设计

仅用加法的实在是想不出来。。 #include <iostream> using namespace::std; using std::cout; using std::cin; int ljq(int n) {if(n < 1){return n;}else{return (nljq(n-1));} }int main() {int n;cin >> n;std::cout << ljq(n);return 0; }

软件测试业务梳理的实用技巧

测试业务梳理 在日常的测试工作中&#xff0c;不知道大家是否会有梳理自己测试业务的习惯。我个人觉得这个事情是值得做的&#xff0c;最好还可以培养成一个习惯。 一、为什么要梳理业务&#xff1f; 因为在业务测试中&#xff0c;作为测试人员&#xff0c;熟悉负责的业务是非…

vue黑马之小黑的书架小案例

今天我为大家带来一个小案例&#xff0c;可以帮助你更好的了解v-for的使用 上代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>小黑的书架</title> </head> <body>…

LabVIEW开发自动驾驶的双目测距系统

LabVIEW开发自动驾驶的双目测距系统 随着车辆驾驶技术的不断发展&#xff0c;自动驾驶技术正日益成为现实。从L2级别的辅助驾驶技术到L3级别的受条件约束的自动驾驶技术&#xff0c;车辆安全性和智能化水平正在不断提升。在这个过程中&#xff0c;车辆主动安全预警系统发挥着关…

话说“俗”的赵本山被曝要重返春晚

据“花边新闻”称“赵本山被曝要重返春晚”&#xff0c;这个消息虽不知是真的还是假的&#xff0c;都能牵动央视春晚的收视率和凡夫俗子的神经&#xff0c;而且更有值得讨论的价值&#xff0c;所以笔者在此唠嗑唠嗑。 图&#xff1a;来源中关村在线 众所周知&#xff0c;具有二…

命令行方式使用abator.jar生成ibatis相关代码和sql语句xml文件

最近接手一个老项目&#xff0c;使用的是数据库是sql server 2008&#xff0c;框架是springmvc spring ibatis&#xff0c;老项目是使用abator插件生成的相关代码&#xff0c;现在需要增加新功能&#xff0c;要添加几张新表&#xff0c;可是目前网上下载的abator插件&#xf…

【每日一题】【12.20】2828.判别首字母缩略词

&#x1f525;博客主页&#xff1a; A_SHOWY&#x1f3a5;系列专栏&#xff1a;力扣刷题总结录 数据结构 云计算 数字图像处理 力扣每日一题_ 1.题目链接 2828. 判别首字母缩略词https://leetcode.cn/problems/check-if-a-string-is-an-acronym-of-words/ 2.题目描述 今天…

Excel如何将行的值转换为列值?

问题:Excel如何将行的值转换为列值?(如图左表变成右表) 1.用 SUMIFS(求和区域, 条件区域1, 条件1, [条件区域2, 条件2], ...)函数 比如:=SUMIFS($C$2:$C$8,$A$2:$A$8,H3,$B$2:$B$8,"快车") 2.直接用简单的透视表 (1)随机点击目标目标表格任何位置,点击插入…

Java 中单例模式的常见实现方式

目录 一、什么是单例模式&#xff1f; 二、单例模式有什么作用&#xff1f; 三、常见的创建单例模式的方式 1、饿汉式创建 2、懒汉式创建 3、DCL&#xff08;Double Checked Lock&#xff09;双检锁方式创建 3.1、synchronized 同步锁的基本使用 3.2、使用 DCL 中存在的疑…

如何开发一个prompt?prompt的使用有哪些原则?

提示词使用原则 如何开发一个跟自己预期结果接近的提示词&#xff1f;有哪些基本原则&#xff1f; 提示词迭代开发 写提示词时&#xff0c;第一次尝试是值得的&#xff0c;反复完善提示&#xff0c;获得越来越接近你想要的结果 原文来源于B站吴恩达提示工程教学公开课。…

你知道跨站脚本攻击吗?一篇带你了解什么叫做XSS

1.XSS简介 &#xff08;1&#xff09;XSS简介 XSS作为OWASP TOP 10之一。 XSS中文叫做跨站脚本攻击&#xff08;Cross-site scripting&#xff09;&#xff0c;本名应该缩写为CSS&#xff0c;但是由于CSS&#xff08;Cascading Style Sheets&#xff0c;层叠样式脚本&#x…

软件测试真的看不到前途吗?

看到这个问题和问题后的说明&#xff0c;题主的显性问题就有两个&#xff1a; 1、软件测试有没有前途 2、若从事没有前途的测试&#xff0c;该怎么办&#xff08;真的只能转行么&#xff09;&#xff1f; 一、软件测试有没有前途&#xff1f; 先说结论&#xff1a;如果在2020年…