第7章链接:如何动态连接共享库、从应用程序中加载和链接共享库

news2024/11/27 2:32:19

文章目录

  • 7.10 动态链接共享库
    • 静态库的缺点
    • 何为共享库
    • 共享库的"共享"的含义
    • 动态链接过程
  • 7.11 从应用程序中加载和链接共享库
    • 运行时动态加载和连接共享库的接口 dlopen
    • 函数 dlsym
    • 函数 dlclose
    • 函数 dlerror
    • 动态加载和链接共享库的应用程序示例

7.10 动态链接共享库

静态库的缺点

  1. 和所有的软件一样,需要定期维护和更新。如果应用程序员想要使用一个库的最新版本,他们必须以某种方式了解到该库的更新情况,然后显式地将它们的程序与新的库重新链接。
  2. 几乎每个 C 程序都使用标准 I/O 函数,比如 prinfscanf。在运行时,这些函数的代码会被复制到每个运行进程的文本段中。在一个运行 50 ~ 100 个进程的典型系统中,这会是对稀少的存储器资源的极大浪费。(存储器的一个有趣属性就是不论一个系统中有多大的存储器,它总是一种稀有的资源。磁盘空间和厨房的垃圾桶同样有这种属性。)

何为共享库

共享库(shared library)是致力于解决静态库缺陷的一个现代创新产物。共享库是一个目标模块,在运行时,可以加载到任意的存储器地址,并在存储器中和一个程序链接起来。这个过程称为动态链接(dynamic linking),是由一个叫做动态链接器(dynamic linker)的程序来执行的。

共享库也称为共享目标(shared object),在 Unix 系统中通常用 .so 后缀来表示。微软的操作系统大量地利用了共享库,它们称为 DLL (动态链接库)。

共享库的"共享"的含义

共享库的“共享” 在两个方面有所不同。

  • 首先,在任何给定的文件系统中,对于一个库只有一个 .so 文件。所有引用该库的可执行目标文件共享这个 .so 文件中的代码和数据,而不是像静态库的内容那样被拷贝和嵌入到引用它们的可执行的文件中。
  • 其次,在存储器中,一个共享库的 .text 节只有一个副本可以被不同的正在运行的进程共享。

动态链接过程

下图是如下程序的动态链接过程:
在这里插入图片描述
在这里插入图片描述
为了构造图7.5 中向量运算示例程序的共享库 libvector.so,会调用编译器,给链接器如下特殊指令:

# -fPIC 选项指示编译器生成与位置无关的代码
# -shared 选项指示链接器创建一个共享的目标文件
unix> gcc -shared -fPIC -o libvector.so addvec.c multvec.c

一旦创建了这个库,随后就要将它链接到图 7.6 的示例程序中。

unix> gcc -o p2 main2.c ./libvector.so

这样就创建了一个可执行目标文件 p2,而此文件的形式使得它在运行时可以和 libvector.so 链接。

基本思路是当创建可执行文件时,静态执行一些链接,然后在程序加载时,动态完成链接过程。

认识到这一点是很重要的:在此时刻,没有任何 libvector.so 的代码和数据节被真的拷贝到可执行文件 p2 中。取而代之的是,链接器拷贝了一些重定位和符号表信息,它们使得运行时可以解析对 libvector.so 中代码和数据的引用。

当加载器加载和运行可执行文件 p2 时,它利用 7.9 节讨论过的技术,加载部分链接的可执行文件 p2。

接着,它注意到 p2 包含一个 .interp 节,这个节中包含动态链接器的路径名,动态链接器本身就是一个共享目标(比如,在 Linux 系统上的 LD-LINUX.SO)。加载器不再像它通常那样将控制传递给应用,取而代之的是加载和运行这个动态链接器。

然后,动态链接器通过执行下面的重定位完成链接任务:

  • 重定位 libc.so 的文本和数据到某个存储器段。在 IA32/Linux 系统中,共享库被加载到从地址 0x40000000 开始的区域中(见第7章链接:重定位、可执行目标文件、加载可执行目标文件中的图7.13)
  • 重定位 libvector.so 的文本和数据到另一个存储器段。
  • 重定位 p2 中所有对由 libc.solibvector.so 定义的符号的引用。

最后,动态链接器将控制传递给应用程序。从这个时刻开始,共享库的位置就固定了,并且在程序执行的过程中都不会改变。

7.11 从应用程序中加载和链接共享库

到此刻为止,已经讨论了在应用程序执行之前,即应用程序被加载时,动态链接器加载和链接共享库的情景。然而,应用程序还可能在它运行时要求动态链接器加载和链接任意共享库,而无需在编译时链接那些库到应用中。

动态链接是一项强大有用的技术。下面是一些现实的例子:

  • 分发软件。微软 Windows 应用的开发者常常利用共享库来分发软件更新。他们生成一个共享库的版本,然后用户可以下载,并用它替代当前的版本。下一次他们运行应用程序时,应用将自动链接和加载新的共享库。
  • 构建高性能 Web 服务器。许多Web服务器生成动态内容,比如个性化的 Web 页面、账户余额和广告标语。早期的 Web 服务器通过使用 forkexecve 创建一个子进程,并在该子进程的上下文中运行 CGI 程序,来生成动态内容。然而,现代高性能的 Web 服务器可以使用基于动态链接的更有效和完善的方法来生成动态内容。

其思路是将生成动态内容的每个函数打包在共享库中。当一个来自 Web 浏览器的请求到达时,服务器动态地加载和链接适当的函数,然后直接调用它,而不是使用 forkexecve 在子进程的上下文中运行函数。函数会一直缓存在服务器的地址空间中,所以只要一个简单的函数调用的开销就可以处理随后的请求了。这对一个繁忙的网站来说是有很大影响的。更进一步,可以在运行时,无需停止服务器,更新已存在的函数,以及添加新的函数。

运行时动态加载和连接共享库的接口 dlopen

像 Linux 和 Solaris 这样的 Unix 系统,为动态链接器提供了一个简单的接口,允许应用程序在运行时加载和链接共享库。

#include <dlfcn.h>

//返回:若成功则为指向句柄的指针,若出错则为Null
void *dlopen(const char *filename, int flag);
  • dlopen 函数加载和链接共享库 filename。用以前带 RTLD_GLOBAL 选项打开的库解析 filename 中的外部符号。如果当前可执行文件是带 -rdynamic 选项编译的,那么对符号解析而言,它的全局符号也是可用的。

  • flag 参数必须要么包括 RTLD_NOW,该标志告诉链接器立即解析对外部符号的引用,要么包括 RTLD_LAZY 标志,该标志指示链接器推迟符号解析直到指向来自库中的代码时。这两个值中的任意一个都可以和 RTLD_GLOBAL 标志取或。

函数 dlsym

#include <dlfcn.h>

//返回:若成功则为指向符号的指针,若出错则为Null
void *dlsym(void *handle, char *symbol);

dlsym 函数的输入是一个指向前面已经打开共享库的句柄和一个符号名字,如果该符号存在,就返回符号地址,否则返回 NULL。

函数 dlclose

#include <dlfcn.h>

//返回:若成功则为0,若出错则为1
int dlclose(void *handle);

如果没有其他共享库还在使用这个共享库,dlclose 函数就卸载该共享库。

函数 dlerror

#include <dlfcn.h>

//返回:如果前面对dlopen、dlsym 或 dlclose 的调用失败,则为错误消息,如果前面的调用成功,则为NULL
const char *dlerror(void);

dlerror 函数返回一个字符串,它描述的是调用 dlopendlsym 或者 dlclose 函数时发生的最近的错误,如果没有错误发生,就返回 NULL。

动态加载和链接共享库的应用程序示例

下面的程序展示了如何利用这个接口动态链接到 libvector.so 共享库,然后调用它的 addvec 程序。

//dll.c
//一个动态加载和链接共享库 libvector.so 的应用程序
#include <stdio.h>
#include <dlfcn.h>

int x[2] = {1, 2};
int y[2] = {3, 4};

int z[2];

int main()
{
	void *handle;
	void (*addvec)(int *, int *, int *, int);
	char *error;

	/* dynamically load the shared library that contains addvec() */
	handle = dlopen("./libvector.so", RTLD_LAZY);
	if (!handle) {
		fprintf(stderr, "%s\n", dlerror());
		exit(1);
	}

	/* get a pointer to the addvec() function we just loaded */
	addvec = dlsym(handle, "addvec");
	if ((error = dlerror()) != NULL)  {
		fprintf(stderr, "%s\n", error);
		exit(1);
	}
	
	/* Now we can call addvec() just like any other function */
	addvec(x, y, z, 2);
	printf("z = [%d %d]\n", z[0], z[1]);
	
	/* unload the shared library */
	if (dlclose(handle) < 0) {
		fprintf(stderr, "%s\n", dlerror());
		exit(1);
	}

	return 0;
}

要编译这个程序,将以下面的方式调用 GCC:

unix> gcc -rdynamic -O2 -o p3 main3.c -ldl

旁注:共享库和 Java 本地接口

Java 定义了一个标准调用规则,叫做 Java 本地接口(Java Native Interface,JNI),它允许 Java 程序调用 “本地的” C 和 C++ 函数。 JNI 的基本思想是将本地 C 函数,比如说 foo,编译到共享库中,比如说 foo.so。当一个正在运行的 Java 程序试图调用函数 foo 时,Java 解释程序利用 dlopen 接口(或者某个类似于此的东西)动态链接和加载 foo.so,然后再调用 foo。

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

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

相关文章

强化学习路线规划之深度强化学习代码

虽然说很多代码都有问题&#xff0c;但是不管它们&#xff0c;我不是为了去debug&#xff0c;紧盯住自己的目标&#xff0c;目标是整理出一条通常的强化学习之路&#xff0c;让自己以及看到这些博客的大家在学习的时候能够少走一些弯路。所以从q-learning和Sarsa开始&#xff0…

buuctf9

目录 web [ZJCTF 2019]NiZhuanSiWei misc [BJDCTF2020]认真你就输了 刷新过的图片 crypto 篱笆墙的影子 RSA web [ZJCTF 2019]NiZhuanSiWei 1.启动环境 <?php $text $_GET["text"]; $file $_GET["file"]; $password $_GET["password…

UE4与MATLAB联合仿真环境配置中遇到的问题及解决办法

UE4与MATLAB联合仿真环境配置中遇到的问题及解决办法 目录 UE4与MATLAB联合仿真环境配置中遇到的问题及解决办法前言问题及解决办法1. The following modules are missing or built with a different engine version: MathWorksAerospace MathWorksSimulation MathWorksUAV Eng…

IMX6Ull内核移植详细过程讲解

文章目录 一、安装相应依赖包二、下载相应的内核版本库&#xff08;1&#xff09;讲解官网内核分支&#xff08;2&#xff09;下载内核版本库&#xff08;3&#xff09;内核目录文件讲解 三、开始内核移植过程&#xff08;1&#xff09;步骤一、修改默认架构和默认交叉编译器&a…

modbus协议与nodejs modbus-serial实现

nodejs可通过modbus-serial库来实现modbus协议 npm install modbus-srial 安装完后在examples目录下有例子说明如何使用&#xff0c;本文主要讲述作为客户端的使用方式。 polling_RTU是使用485串口来实现modbus通信&#xff0c; polling_TCP和 polling_UDP则使用TCP/UDP来实现…

HBuilderX使用

HBuilderX使用&#xff08;Vue前后端分离&#xff09; 概述&#xff1a;DCloud开发者后台 DAccount Service 1、官网下载开发工具&#xff1a;HBuilderX-高效极客技巧 注意&#xff1a;安装目录路径中不能出现中文特殊字符&#xff0c;否则会造成项目无法编译。比如C:/Progr…

learn_C_deep_10 深刻认识C语言重点操作符

目录 注释符号 其他注释方法 注释建议 接续符和转义符 在续行符\之前或者之后带上空格&#xff0c;行不行&#xff1f; \n&#xff1a;换行符与\r&#xff1a;回车符之间的区别 两个小程序(旋转光标、倒计时) 单引号和双引号 ​编辑abcd这样写有问题吗? 特殊情况 为…

ThinkPHP6表单上传的数据获取的四种方式【请求对象调用,静态调用,助手函数调用,原生的get|post】

ThinkPHP6表单上传的数据获取 首先一个form表单中的Input控件需要存在对应的name值&#xff0c;然后method为post|get,action为要提交到的控制器的哪个方法 例如(本文的前端视图代码均为下面的html)&#xff1a; <form method"post" action"/index.php/lo…

Hard Patches Mining for Masked Image Modeling

摘要 蒙面图像建模&#xff08;MIM&#xff09;因其在学习可伸缩视觉表示方面的潜力而引起了广泛的研究关注。在典型的方法中&#xff0c;模型通常侧重于预测掩码补丁的特定内容&#xff0c;并且它们的性能与预定义的掩码策略高度相关。直观地说&#xff0c;这个过程可以被看作…

应用架构总结

架构目标 高可用性 整体系统可用性最低99.9%&#xff0c;目标99.99%。全年故障时间整个系统不超过500分钟&#xff0c;单个系统故障不超过50分钟。 高可扩展性 系统架构简单清晰&#xff0c;应用系统间耦合低&#xff0c;容易水平扩展&#xff0c;业务功能增改方便快捷。 低成…

IOS APP universal links 申请流程

一、背景 项目框架&#xff1a;APP h5 需求&#xff1a;APP首页可以选择微信&#xff0c;进行授权登录。 问题&#xff1a;在APP跳转到微信进行授权的时候&#xff0c;提示“由于应用universal link校验不通过&#xff0c;无法完成微信登录”。 二、申请流程 1. 苹果后台管…

XML语言简介和语法介绍

XML语言简介 文章目录 XML语言简介用途各部分注解声明元素属性注释CDATA转义字符 可拓展标记语言&#xff1b;是SGML的子集&#xff0c;可以描述很复杂的数据关系 用途 用于组织和存储数据&#xff0c;除此之外都和XML本身无关 配置文件(例子&#xff1a;Tomcat的web.xml,se…

Android 消息机制

消息机制相关API Message(消息) 可理解为线程之间通讯的数据单元, 可以通过message携带需要的数据 创建对象: Message.obtain(what) 封装数据 public int what public int arg public Object obj Handler(处理器) Handler是Message的处理器, 同时也负责消息的发送和移除的…

Linux内核(十四)Input 子系统详解 IV —— 配对的input设备与input事件处理器 input_register_handle

文章目录 input_handle结构体详解配对的input设备与input事件处理器实例input核心层对驱动层和事件层之间的框架建立流程图 本文章中与input子系统相关的结构体可参考input子系统结构体解析 input函数路径&#xff1a;drivers/input/input.c input_handle结构体详解 input_ha…

二十六:交易详细信息

功能需求 用户在交易主页面&#xff0c;点击交易名称超级链接&#xff0c;跳转到交易明细页面&#xff0c;完成查看交易明细的功能。 *显示交易的基本信息 *显示交易的备注信息 *显示交易的历史信息 *显示交易的阶段图标信息 流程图 后端代码实现 1.tran TranMapper /*…

实现Fast sigmoid和Softmax

Sigmoid 函数介绍 Sigmoid 函数&#xff08;Logistic 函数&#xff09;是神经网络中非常常用的激活函数&#xff0c;它的数学表示如下: 由于 e x e^x ex幂运算是非常耗时的计算&#xff0c;因此尝试通过替换sigmoid中的 e x e^x ex运算&#xff0c;来提高运行效率&#xff0c;同…

Linux篇4

Shell常用命令 1. 日期时间类1.1 date&#xff1a;日期时间类1.1.1 查看当前日期时间1.1.2 查看非当前日期时间1.1.3 设置系统日期时间 1.2 cal&#xff1a;日历类 2. 用户管理命令2.0 id&#xff1a;查看用户是否存在2.1 useradd&#xff1a;添加新用户2.2 passwd&#xff1a;…

Linux相关问题

中英文切换 super空格切换中英文&#xff1b;super指键盘上的Win键&#xff1b; 开机自启动服务设置 可视化方式&#xff1a;输入setup命令进入自启动服务配置&#xff1b;通过上下键选中服务&#xff0c;通过空格选择是否自启动该服务&#xff1b; 开启不同的终端 CTRLALT…

Jetson nano 之 ROS入门 - - 机器人坐标变换

文章目录 前言一、空间坐标变换原理1. 位姿描述2. 欧拉角与四元数 二、ROS中python实现坐标变换1. 坐标msg消息载体2. 乌龟跟随的程序实现 总结 前言 ROS给开发者们提供了很多集成度很高的开发工具&#xff0c;例如rviz和gazebo。rviz是三维可视化工具&#xff0c;可以显示图像…

【P23】JMeter 用户参数(User Parameters)

&#xff08;1&#xff09;、测试计划右键 <<< 添加 <<< 前置处理器 <<< 用户参数 如图&#xff0c;添加两个变量&#xff0c;每个变量包含两个用户 &#xff08;2&#xff09;、测试计划右键 <<< 添加 <<< 线程&#xff08;用户…