运行时链接

基本概念

运行时链接,是在程序运行时(而非编译时或加载时)将程序代码与其依赖的库代码进行链接的过程。动态链接在程序启动时或实际运行过程中通过API函数完成。这种方式的主要优点是它允许程序在运行时加载和卸载不同的库模块,从而实现更高的模块化和灵活性。

在C语言的上下文中,运行时链接通常通过动态链接库(例如 .so 文件在Unix-like系统中,.dll 文件在Windows系统中)来实现。

以下是运行时链接的基本概念和步骤:

  1. 动态链接库:与静态库(如 .a.lib 文件)不同,动态链接库包含可以被多个程序共享的代码。这意味着可以在多个程序中使用同一份库代码的物理副本。

  2. 延迟加载:程序不需要在启动时加载所有依赖的库。它可以选择在运行时的某个特定点加载需要的库。

  3. API函数:操作系统提供的API函数允许程序在运行时打开、查询和关闭动态链接库。在Unix-like系统中,这些函数通常包括 dlopen(), dlsym(), dlclose()dlerror()

dl**函数族

dlopen(), dlsym(), dlclose(), 和 dlerror() 是在 Unix-like 系统中用于动态加载共享对象(通常是.so文件,在macOS上是.dylib文件)和执行它们的接口的函数。这组API被称为“动态链接器接口”。

  1. dlopen():

    • 用途:打开一个共享对象或可执行文件。
    • 原型:
      void *dlopen(const char *filename, int flag);
      
    • 参数:
      • filename:要加载的共享对象的路径。
      • flag:定义如何加载和解析对象。常见的标志包括 RTLD_LAZY(在初次调用时解析符号)和 RTLD_NOW(立即解析所有符号)。
    • 返回值:返回一个句柄,该句柄在后续的调用(如dlsym()dlclose())中使用。如果有错误,返回NULL。
  2. dlsym():

    • 用途:获取共享对象中函数或变量的地址。
    • 原型:
      void *dlsym(void *handle, const char *name);
      
    • 参数:
      • handle:由 dlopen() 返回的句柄。
      • name:要查找的符号的名称。
    • 返回值:返回符号的地址。如果有错误,返回NULL。
  3. dlclose():

    • 用途:关闭 dlopen() 打开的共享对象,并减少其引用计数。
    • 原型:
      int dlclose(void *handle);
      
    • 参数:
      • handle:由 dlopen() 返回的句柄。
    • 返回值:成功时返回0,失败时返回非零值。
  4. dlerror():

    • 用途:返回描述上一次调用 dlopen(), dlsym(), 或 dlclose() 时出现的错误的字符串。
    • 原型:
      char *dlerror(void);
      
    • 返回值:如果没有错误,返回NULL。否则,返回描述错误的字符串。

使用这些函数可以在运行时动态地加载、链接和调用共享库中的函数,而不需要在编译时静态地链接它们。这为编写可插拔和可扩展的代码提供了很大的灵活性。

综合案例

以下是一个简单的示例,演示了如何使用 dlopen(), dlsym(), dlclose(), 和 dlerror() 来动态加载一个共享库,并调用其中的函数。

假设我们有一个共享库 libgreeting.so,其中包含一个函数 void greet(const char* name)

libgreeting.c:

#include <stdio.h>void greet(const char* name) {printf("Hello, %s!\n", name);
}

可以使用以下命令编译此共享库:

gcc -shared -o libgreeting.so libgreeting.c -fPIC

现在,我们将写一个程序来动态加载这个共享库,并调用 greet() 函数:

main.c:

#include <stdio.h>
#include <dlfcn.h>int main() {void* handle;void (*greet_func)(const char*);char* error;handle = dlopen("./libgreeting.so", RTLD_LAZY);if (!handle) {fprintf(stderr, "%s\n", dlerror());return 1;}// 清除现有的错误,如果有的话dlerror();greet_func = (void (*)(const char*)) dlsym(handle, "greet");if ((error = dlerror()) != NULL)  {fprintf(stderr, "%s\n", error);return 1;}greet_func("world");dlclose(handle);return 0;
}

编译主程序:

gcc main.c -o main -ldl

运行 main,应该看到输出 Hello, world!

这个示例展示了如何使用动态链接器接口来在运行时加载共享库,并动态地调用其中的函数。这为编写灵活、可插拔的程序提供了可能性。

注1:

gcc -shared -o libgreeting.so libgreeting.c -fPIC

这个命令是使用 gcc 编译器来生成一个共享库。让我们一步步地解析这个命令:

  1. gcc:

    • 这是GNU C编译器。它用于编译C程序。
  2. -shared:

    • 这个选项告诉编译器我们希望生成一个共享库,而不是可执行文件。共享库(在Linux中通常是 .so 文件,在macOS中是 .dylib 文件)可以被多个程序共享并在运行时动态加载。
  3. -o libgreeting.so:

    • -o 选项用于指定输出文件的名称。在这里,我们要生成的共享库名称为 libgreeting.so
  4. libgreeting.c:

    • 这是我们要编译的源文件。它包含了我们的代码,特别是 greet 函数的实现。
  5. -fPIC:

    • 这是一个编译选项,意为“生成位置无关代码(Position Independent Code)”。位置无关代码意味着生成的代码可以在任何地址执行,这对于共享库是必要的,因为我们不知道加载它的程序将把它放在何处。
    • 当创建共享库时,使用 -fPIC 是很重要的,因为它确保库中的代码不依赖于任何固定的地址。

总之,这个命令告诉 gcclibgreeting.c 源文件生成一个名为 libgreeting.so 的共享库,并确保生成的代码是位置无关的。

注2:

gcc main.c -o main -ldl

-ldl 是一个 gcc 编译选项,用于链接程序时链接到特定的库。在这种情况下,它告诉链接器链接到动态链接器的库。具体来说,-l 选项告诉链接器链接到一个库,而 dl 指定了库的名字(在这里是 libdl.solibdl.a)。

这里的 “dl” 指的是 “dynamic linking”(动态链接),它是一个库,提供了用于动态加载共享对象、查询它们的符号等操作的函数。这就是我们在前面的例子中使用 dlopen(), dlsym(), dlclose(), 和 dlerror() 函数的原因。

为了能够在程序中使用这些函数,并在运行时解析它们的地址,需要链接到 libdl。这就是为什么我们需要 -ldl 选项。

简而言之:

  • -l: 告诉 gcc 我们要链接到一个库。
  • dl: 指定我们要链接到的库的名字,即动态链接库(libdl)。

因此,-ldl 确保我们的程序被正确地链接到 libdl,从而可以使用动态链接相关的函数。


总之,运行时链接提供了以下优点:

  • 共享代码:多个程序可以共享同一个库的物理副本。
  • 模块化:程序可以根据需要加载或卸载特定模块。
  • 更新和修补:可以通过替换库文件来更新或修补程序的部分,而无需重新编译或重新链接整个程序。

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

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

相关文章

在Python中解决自定义类型比较的问题

1 问题原因 在Python编程中&#xff0c;当我们尝试对自定义类型进行比较时&#xff0c;可能会遇到这样的错误&#xff1a;TypeError: < not supported between instances of User and User 这个错误的原因是Python不知道如何对你的自定义类型进行比较。为了解决这个问题&a…

redisson常用api

redisson提供了很多对象类型的api&#xff0c;下面介绍下一些常用的对象api。 RBucket 可操作任何对象的api&#xff0c;前提是要确定好泛型&#xff0c;方法比较少。大小限制为512Mb。 RBucket<AnyObject> bucket redisson.getBucket("anyObject");bucket…

vue3中的吸顶导航交互实现 | VueUse插件

目的&#xff1a;浏览器上下滚动时&#xff0c;若距离顶部的滚动距离大于78px&#xff0c;吸顶导航显示&#xff0c;小于78px隐藏。使用vueuse插件中的useScroll方法​​​​​​​和动态类名控制进行实现 1. 安装 npm i vueuse/core 2. 获得滚动距离 项目中导入&#xff0…

@Resource 注入为null 的解决方法

Resource Resource可以用于注入对象 一般我们在编码中都会使用Resource来注入一个实例对象&#xff0c;但是特殊情况下可能会是null。 这个时候可以用SpringUtil.getBean()来手动获取 代码示例 private HbaseProperties hbaseProperties SpringUtil.getBean(HbasePropertie…

在python程序中用windows的icon

这个exe的弹窗功能会使用到一个ico文件&#xff0c;如图&#xff1a; 用软件GreenfishIconEditorProPortable或者使用在线软件将你需要的图片制作成windows的icon 用程序将ico文件生成文本文件 import base64picture_name "logo.ico" open_pic open("%s…

【100天精通Python】Day56:Python 数据分析_Pandas数据清洗和处理(删除填充插值,数据类型转换,去重,连接与合并)

目录 数据清洗和处理 1.处理缺失值 1.1 删除缺失值&#xff1a; 1.2 填充缺失值&#xff1a; 1.3 插值&#xff1a; 2 数据类型转换 2.1 数据类型转换 2.2 日期和时间的转换&#xff1a; 2.3 分类数据的转换&#xff1a; 2.4 自定义数据类型的转换&#xff1a; 3 数…

神经反馈设备使用感受2:采集Muse的EEG原始数据(转自知乎)

神经反馈设备使用感受2&#xff1a;采集Muse的EEG原始数据 转自知乎&#xff0c;内容很好&#xff0c;怕之后找不到 想了一下&#xff0c;单写一部分来介绍一下Muse在数据采集方面的操作。同时也解释一下我自己的EEG数据是从哪里采集的。 关于Muse EEG数据的精度&#xff0c;在…

vue2实现自定义主题webpack-theme-color-replacer

需求&#xff1a;根据element的自定义主题色&#xff0c;之后改变element的全局所有颜色&#xff0c;解决页面刷新后主题色失效问题&#xff0c;这个需要把颜色存入到浏览器的存储中&#xff0c;如果换个浏览器就得重新选择了哈&#xff0c;如果需要在不同的浏览器保持一致的主…

将AI融入到SEO中—基于Python的实现思路

在当今数字化时代&#xff0c;搜索引擎优化&#xff08;SEO&#xff09;对于网站和在线业务的成功至关重要。然而&#xff0c;随着人工智能&#xff08;AI&#xff09;技术的迅猛发展&#xff0c;我们可以利用它来提升SEO策略并取得更好的效果。本文将介绍如何通过使用Python编…

WordPress wp-file-manager 文件上传漏洞 CVE-2020-25213

1.漏洞复现 WordPress 6.2 插件&#xff1a;wp-file-manager 6.0&#xff0c;File Manager (advanced view) – WordPress plugin | WordPress.org &#xff08;https://wordpress.org/plugins/wp-file-manager/advanced/&#xff09; 复现 后台&#xff0c;安装、启动插件…

分布式系统第五讲:分布式事务及实现方案

分布式系统第五讲&#xff1a;分布式事务及实现方案 事务是一个程序执行单元&#xff0c;里面的所有操作要么全部执行成功&#xff0c;要么全部执行失败。而分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。本…

(二)程序语言设计基础

目录 1. 程序设计语言概述 1.1 基本概念 1.1.1 低级语言和高级语言 1.1.2 编译程序和解释程序 1.1.3 程序设计语言的定义 1.1.4 程序设计语言的分类 1.2 程序设计语言的基本成分 1.2.1 数据成分 1.2.2 运算成分 1.2.3 控制成分 1.2.4 传输成分 1.2.5 函数 2. 语言…

【LeetCode: 1462. 课程表 IV:拓扑排序+图+广度优先搜索】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

现场直击 | 国台国标·中秋礼酒惊艳闪耀酒博会

以酒为媒&#xff0c;以酒会友。 9月9日&#xff0c;以“展示全球佳酿&#xff0c;促进开放合作”为主题的第12届中国&#xff08;贵州&#xff09;国际酒类博览会&#xff08;以下简称“贵州酒博会”&#xff09;在贵阳国际会议展览中心重磅开幕&#xff0c;本届贵州酒博会吸…

狼的传说小游戏

欢迎来到程序小院 狼的传说 玩法&#xff1a; 鼠标左键选择能防御、战斧、风暴3%、滚石10%、藤曼5%、冰柱5%、飞跃10%、三叶草20%、钢叉15%&#xff0c;消灭所有敌人&#xff0c;不同关卡不同敌人&#xff0c;快去闯关消灭敌人吧^^。开始游戏https://www.ormcc.com/play/gameS…

MySQL——读写分离

简介 读写分离&#xff0c;基本的原理是让主数据库处理事务性增、改、删操作&#xff08;INSERT、UPDATE、DELETE&#xff09;&#xff0c;而从数据库处理SELECT查询操作。数据库复制被用来把事务性操作导致的变更同步到集群中的从数据库。一般来说都是通过 主从复制&#xff…

得帆云“智改数转,非同帆响”-AIGC+低代码PaaS平台系列白皮书,正式发布!

5月16日下午&#xff0c;由上海得帆信息技术有限公司编写&#xff0c;上海市工业互联网协会指导的以“智改数转&#xff0c;非同帆响”为主题的《得帆云 AIGC低代码PaaS平台系列白皮书》正式在徐汇西岸国际人工智能中心发布。 本次发布会受到了上海市徐汇区政府、各大媒体和业内…

如何实现一个数据库的 UDF?图数据库 NebulaGraph UDF 功能背后的设计与思考

大家好&#xff0c;我是来自 BOSS直聘的赵俊南&#xff0c;主要负责安全方面的图存储相关工作。作为一个从 v1.x 用到 v3.x 版本的忠实用户&#xff0c;在见证 NebulaGraph 发展的同时&#xff0c;也和它一起成长。 BOSS直聘和 NebulaGraph 关于 NebulaGraph 在 BOSS直聘的应…

9.13 | day 6 |day 45| to 完全平方数

● 70. 爬楼梯 &#xff08;进阶&#xff09; class Solution {public int climbStairs(int n) {int[] dp new int[n1];//设置背包容量&#xff1a;n个int m 2;//有两个物品&#xff0c;注意这是一个完全背包问题dp[0] 1;//initialize ​for(int i 1;i<n;i){//遍历背包f…

快速幂 c++

一般大家写都是 int ans 1; for (int i 1; i < a; i )ans * x;时间复杂度 但是这对于我们还不够&#xff0c;我们要 首先我们得知道一个数学知识 那么求 就有以下递归式 a 能被2整除 a 不能被2整除 (这里a/2是整除) 所以每次都调用 不就是么 最后补充一个东西…