深入Os--动态链接

1.动态链接库的使用
动态库支持以两种模式使用,一种模式下,在程序加载运行时,完成动态链接。一种模式下,在程序运行中,完成动态链接。
1.1.程序加载运行时完成动态链接
我们通过一个实例介绍程序加载运行时,使用动态库的方式
在这里插入图片描述

(1). 构建动态库
动态库源文件及makefile位于dynamic
a.t1.cpp

// t1.cpp
int addcnt = 0;
void addvec(int *x, int *y, int *z, int n)
{int i;addcnt++;for (i = 0; i < n; i++)z[i] = x[i] + y[i];
}

b.t2.cpp

// t2.cpp
int mulcnt = 0;
void multvec(int *x, int *y, int *z, int n)
{int i;mulcnt++;for (i = 0; i < n; i++)z[i] = x[i] * y[i];
}

c.makefile

main: t1 t2 dynamict1:g++ -fpic -std=c++11 t1.cpp -c
t2:g++ -fpic -std=c++11 t2.cpp -cdynamic:g++ -std=c++11 -shared t1.o t2.o -o libt.soclean:rm *.o libt.so *.txt

d.通过执行make完成构建。注意编译动态库源文件时需指定-fpic,基于.o得到动态库需指定-shared

(2).提供动态库导出符号声明文件
动态库导出符号声明文件放在include。
a.t.h

#ifndef _T_H
#define _T_H
extern int addcnt;
void multvec(int *x, int *y, int *z, int n);
void addvec(int *x, int *y, int *z, int n);
#endif

上述除了导出函数,我们还导出了变量addcnt。变量的声明需加上extern,否则会被视为变量定义。

(3).主程序使用动态库导出符号
a.主程序为main.cpp

#include <stdio.h>
#include "t.h"int x[2] = {1, 2};
int y[2] = {3, 4};
int z[2];int main()
{addvec(x, y, z, 2);printf("z=[%d %d]\n", z[0], z[1]);printf("addcnt_%d\n", addcnt);return 0;
}

我们采用加载运行时完成动态链接方式使用动态库时,在使用动态库导出符号时,需要先声明符号。然后直接使用即可。
上述使用了动态库导出的addvec,addcnt
b.构建可执行程序的makefile

main:g++ main.cpp -std=c++11 -I./include -L./dynamic -lt
clean:rm a.out *.o *.txt

我们采用加载运行时完成动态链接方式使用动态库时,构建可执行程序时,需通过-L -l来指定要链接的动态库的位置信息。-I用于指定编译期间头文件搜索路径。

(4).启动可执行程序
若上述编译完毕后,我们直接在a.out所在目录通过命令行执行./a.out是不行的。
在这里插入图片描述
因为类似编译链接过程需通过-L -l来指定要链接的动态库的位置信息。加载运行时,可以通过设置LD_LIBRARY_PATH来指定要链接的动态库的位置信息。上述结构下,我们提供s.sh。

// s.sh
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./dynamic
./a.out

这样执行./s.sh即可正常启动。LD_LIBRARY_PATH用于在程序启动运行时告知搜索程序依赖的动态库的路径。
要查看可执行程序依赖那些动态库,可使用ldd a.out
在这里插入图片描述

1.2.程序运行期间完成动态链接
我们通过一个实例介绍程序运行期间,使用动态库的方式。
在这里插入图片描述
(1). 构建动态库
和1.1部分相同。

(2).主程序中使用动态库导出符号
注意,运行期间使用动态库时,我们并不需要动态库导出符号声明文件。
因为使用导出符号的方式是通过dlsym直接取得导出符号地址后,转换为相应类型后使用。
a.主程序为main.cpp
这里的main.cpp放置在demo下

#include <iostream>
#include <dlfcn.h>int x[2] = {1,2};
int y[2] = {3,4};
int z[2];typedef void (*AddVec)(int*, int*, int*, int);
int main()
{void *handle;AddVec addvec = nullptr;char *error;handle = dlopen("libt.so", RTLD_LAZY);if(!handle){printf("%s\n", dlerror());return 0;}addvec = (AddVec)dlsym(handle, "addvec");if((error = dlerror()) != NULL){printf("%s\n", error);dlclose(handle);return 0;}int* addcnt = (int*)dlsym(handle, "addcnt");if((error = dlerror()) != NULL){printf("%s\n", error);dlclose(handle);return 0;}addvec(x, y, z, 2);printf("z = [%d %d],cnt_%d\n", z[0], z[1], *addcnt);dlclose(handle);return 0;
}

我们采用运行期间完成动态链接的方式使用动态库,在使用动态库导出符号时,通过dlsym取得导出符号地址后,转换为匹配类型后,即可使用。上述使用了动态库导出的addvec,addcnt

b.构建可执行程序的makefile
makefile放置在demo。

main:g++ -std=c++11 -rdynamic main.cpp -I../include -ldl
clean:rm a.out *.o *.txt

我们采用运行期间完成动态链接方式使用动态库时,构建可执行程序时,不需要通过-L -l来指定要链接的动态库的位置信息。因为编译链接过程尚未用到运行期间要链接的动态库。但需指定-rdynamic -ldl,因为我们此时需要链接到服务于运行期间动态连接的动态库dl。
在这里插入图片描述
(3).启动可执行程序
类似的,我们在启动前需通过LD_LIBRARY_PATH来指定dlopen中搜索动态库的路径信息。
我们的放置在demo下的s.sh为

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../dynamic
./a.out

但执行./s.sh时,报错了:
在这里插入图片描述
因为我们采用c++方式编译动态库时,库内addvec的符号实际编译出的符号名称为:
在这里插入图片描述
这是因为c++编译器对编译时,针对函数类型会结合其形参为其构建符号名称。c编译器不会。
c++支持同名函数重载,所以,这样是需要的。c不支持同名函数重载,所以,不需要。

上述报错是因为我们通过dlsym取出addvec符号地址时,通过名称addvec在动态库中找不到匹配的符号。
为了正常使用dlsym取得导出符号地址:
(1).我们要么将dlsym传入的符号名修改为_Z6addvecPiS_S_i
(2).要么通过设置使得c++编译时,针对addvec导出符号不要采用符号重新命名机制。我们只需在动态库源文件符号定义处,添加extern "C"修饰即可。若我们采取了此种方式,应该同步在类库导出符号声明文件中为addvec的声明也添加extern "C"修饰。这样,1.1中使用动态库时,也会直接采用addvec来在动态库中定位符号的定义位置。

针对变量类型导出符号,如addcnt,c++编译器不会对符号执行重新命名。所以,直接使用符号名即可。

值得注意的是,添加extern "C"后由于关闭结合形参重命名机制,所以,此时也就不允许同名符号重载了。

int addcnt = 0;
void addvec(int *x, int *y, int *z, int n)
{int i;addcnt++;for (i = 0; i < n; i++)z[i] = x[i] + y[i];
}void addvec(int *x, int *y, int *z)
{int i;i = 0;i++;
}

上述内容,作为t1.cpp内容时,可正常编译。

int addcnt = 0;
extern "C" void addvec(int *x, int *y, int *z, int n)
{int i;addcnt++;for (i = 0; i < n; i++)z[i] = x[i] + y[i];
}extern "C" void addvec(int *x, int *y, int *z)
{int i;i = 0;i++;
}

上述内容作为t1.cpp内容时,无法编译通过。因为存在同名符号问题。
在这里插入图片描述

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

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

相关文章

【Pandas思考记录】力扣181. 超过经理收入的员工

原题链接 Pandas 代码&#xff1a; import pandas as pddef find_employees(employee: pd.DataFrame) -> pd.DataFrame:merged_df pd.merge(employee, employee, left_onmanagerId, right_onid, howinner, suffixes(, _manager))print("merged_df", merged_df)#…

Socket.D 网络应用协议,首版发布!

有用户说&#xff0c;“Socket.D 之于 Socket&#xff0c;尤如 Vue 之于 Js、Mvc 之于 Http” 主要特性 基于事件&#xff0c;每个消息都可事件路由所谓语义&#xff0c;通过元信息进行语义描述流关联性&#xff0c;有相关的消息会串成一个流语言无关&#xff0c;使用二进制输…

【debug】Image 库 字体问题

可能的报错信息&#xff1a; from PIL import ImageFont, ImageDrawdraw ImageDraw.Draw(image)# use a bitmap font font ImageFont.load("arial.pil")draw.text((10, 10), "hello", fontfont)# use a truetype font font ImageFont.truetype("a…

4G基站BBU、RRU、核心网设备

目录 前言 基站 核心网 信号传输 前言 移动运营商在建设4G基站的时候&#xff0c;除了建设一座铁塔之外&#xff0c;更重要的是建设搭载铁塔之上的移动通信设备&#xff0c;这篇博客主要介绍BBU&#xff0c;RRU以及机房的核心网等设备。 基站 一个基站有BBU&#xff0c;…

代数学笔记7: 交换群结构定理,群在集合上的作用

交换群结构定理 G ≅ Z / d 1 Z Z / d 2 Z ⋯ Z / d n Z , d 1 ∣ d 2 , ⋯ , d n − 1 ∣ d n G\cong \mathbb{Z}/d_1\mathbb{Z}\times \mathbb{Z}/d_2\mathbb{Z}\times\cdots\times \mathbb{Z}/d_n\mathbb{Z}, \quad d_1|d_2,\cdots,d_{n-1}|d_n G≅Z/d1​ZZ/d2​Z⋯Z/dn​…

Liunx系统使用超详细(四)~文件/文本相关命令1

目录 一、mkdir命令 1.1基本语法 1.2常用示例 1.2.1创建目录 1.2.2创建多级目录 1.2.3设置权限 1.2.4递归修改权限 1.2.5显示帮助信息 二、touch命令 2.1基本语法 2.2常用示例 2.2.1创建新的空文件 2.2.2更新现有文件的访问和修改时间戳 2.2.3创建多个文件 2.2.…

【Midjourney实战】| 新年红包、元宝、灯笼、福袋生成

文章目录 1 红包生成2 灯笼生成3 福袋生成4 元宝生成 1 红包生成 之前我们生成了新年礼盒&#xff0c;这一期我们来生成一些过年时特有的元宝和红包 首先&#xff0c;最重要的是画面主体 red envelope&#xff08;红包&#xff09;颜色红色 黄色的 red and yellow 为了后期方…

跳水比赛(C++)

系列文章目录 进阶的卡莎C++_睡觉觉觉得的博客-CSDN博客数1的个数_睡觉觉觉得的博客-CSDN博客双精度浮点数的输入输出_睡觉觉觉得的博客-CSDN博客足球联赛积分_睡觉觉觉得的博客-CSDN博客大减价(一级)_睡觉觉觉得的博客-CSDN博客小写字母的判断_睡觉觉觉得的博客-CSDN博客纸币(…

解决部署到k8s上的服务返回的msg乱码和控制台乱码的问题

解决部署到k8s上的服务返回的msg乱码和控制台乱码的问题 java -Dfile.encodingutf-8 -jar xxx.jar

Web漏洞分析-SQL注入XXE注入(下)

随着互联网的不断普及和Web应用的广泛应用&#xff0c;网络安全问题愈发引起广泛关注。在网络安全领域中&#xff0c;SQL注入和XXE注入是两个备受关注的话题&#xff0c;也是导致许多安全漏洞的主要原因之一。本博客将深入研究这两种常见的Web漏洞&#xff0c;带您探寻背后的原…

Python中数组去重的方法汇总

Python中数组去重的方法汇总 1. 使用 set2. 使用列表推导式3. 使用 numpy 库4. 使用 pandas 库5. 使用 collections 库的 Counter6. 使用 functools 库的 reduce7. 使用 itertools 库的 groupby总结 在 Python 中&#xff0c;对数组进行去重是常见的操作&#xff0c;以确保数据…

Python 上下文管理器 with 相关用法

Python 上下文管理器 什么是上下文管理器&#xff1f; context manager一个上下文管理器是一个对象它定义了运行时的上下文使用 with 语句来执行 with 语句 with context as ctx:# 使用这个上下文对象 # 上下文对象已经被清除​ 这里我们使用 Python的文件操作 打开一个文…

GNSEC 2022年第8届全球下一代软件工程线上峰会-核心PPT资料下载

一、峰会简介 新一代软件工程是指利用新的理论、方法和技术&#xff0c;在软件开发、部署、运维等过程中&#xff0c;实现软件的可控、可预测、可维护的软件生产方式。它涵盖了多个领域&#xff0c;如软件开发、测试、部署、运维等&#xff0c;旨在提高软件生产效率和质量。 …

103.进程概述

目录 1.并行和并发 区别&#xff1a; 2.PCB 3.进程状态 4. 进程命令 从严格意义上来讲&#xff0c;程序和进程是两个不同的概念&#xff0c;他们的状态&#xff0c;占用的系统资源都是不同的。 程序&#xff1a;程序是一种静态实体&#xff0c;是存储在计算机存储介质上的…

排序算法之二:冒泡排序

冒泡排序的思路 冒泡排序是交换排序 基本思想&#xff1a;所谓交换&#xff0c;就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置&#xff0c;交换排序的特点是&#xff1a;将键值较大的记录向序列的尾部移动&#xff0c;键值较小的记录向序列的前部移动…

判断三角形-第11届蓝桥杯选拔赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第12讲。 判断三角形&#…

AOC computer monitor

【窗口增亮】关闭就没掉了

Java异常讲解

&#x1f435;本篇文章将对异常相关知识进行讲解 一、异常的结构 在程序执行的过程中出现的一些问题叫做异常&#xff0c;异常其实是一个一个类&#xff0c;每一种异常都代表一个类 1.1 几种常见的异常 System.out.println(10/0); //算数异常 //Exception in thread "m…

LDAP协议和AD活动目录的讲解

目录 LDAP协议 LDAP基本概念 LDAP目录的数据结构 LDAP交互过程以及相关报文 AD&#xff08;Active Directory&#xff09; AD基本概念 AD域与工作组、本地组的区别 AD DS&#xff08;AD域服务&#xff09; 信任关系 组策略和安全组 LDAP协议 LDAP基本概念 LDAP&…

TypeScript:JavaScript的超集,让代码更安全、更可维护

一、介绍 TypeScript是微软开发的一种静态类型检查的JavaScript超集&#xff0c;它在JavaScript的基础上添加了类型系统&#xff0c;使得开发者能够更好地定义变量类型&#xff0c;提高代码的可读性和可维护性。本文将介绍TypeScript的基本概念、优势、使用方法和实践。 二、…