当前位置: 首页 > news >正文

通俗举例讲解动态链接】静态链接

参考动态链接 - 知乎

加上我自己的理解,比较好懂,但可能在细节方面有偏差,但总体是一致的

静态链接的背景

静态链接使得不同的程序开发者和部门能够相对独立的开发和测试自己的程序模块,从某种意义上来讲大大促进了程序开发的效率,原先现在程序规模也随之扩大。
但静态链接的缺点也暴露出来:浪费内存、磁盘空间、模块更新困难(耦合性太强)。

内存与磁盘空间

静态链接在计算机早期还是比较流行的,但是到了后面,其缺点也非常明显。比如浪费内存和磁盘空间,更新模块困难等。

一个文件的转换进程

hello.c(源程序[文本])->预处理器(cpp)->hello.i(修改了的源程序[文本])->编译器(ccl)->hello.s(汇编程序[文本])->汇编器(as)->hello.o(可重定位目标程序[二进制])->链接器(ld)->hello(可执行目标程序[二进制])

只需要注意:

hello.c:源文件

hello.o:由hello.c编译而来

hello(可执行目标程序[二进制]),最终执行文件。

比如我有a.cpp ,a.h,b.cpp c.cpp四个文件

a.cpp 定义了一个函数sayHello

#include <iostream>
void sayHello(){std::cout<<"hello world!";
}

a.h

#ifndef UNTITLED1_A_H
#define UNTITLED1_A_Hvoid sayHello();#endif //UNTITLED1_A_H

b.cpp 调用了a的函数sayHello

#include "a.h"
int main(){sayHello();
}

c.cpp 调用了a的函数sayHello

#include "a.h"
int main(){sayHello();
}

静态链接

如果运行b那么就会结合b.o,以及其调用的a.o生成可执行文件。c运行会另外调用 c.o,a.o生成可执行文件,此时可执行文件可以认为长这样

#include <iostream>
void sayHello(){std::cout<<"hello world!";
}
int main(){sayHello();
}

此时a.o重复了两次,但这两个a.o保存了相同的内容,浪费内存和磁盘。

再比如Program1 & Program2分别包含Program1.o和Program2.o两个模块,并且还共用了Lib.o这个模块。静态链接下,P1和P2都用到了Lib.o这模块,所以它们同时在链接输出的可执行文件P1和P2有两个副本,当同时运行两个程序,Lib.o在磁盘和内存中都有两个副本,浪费空间。

程序开发与发布

静态链接另一个问题是对程序的更新,部署和发布也会很麻烦,我如果在a中更新了sayHello,那么生成的b,c可执行文件里的sayHello还是老的,需要重新连接,很麻烦耦合度很高。

程序有任何模块更新,整个程序就要重新链接,发布给用户,

动态链接

dll文件和so文件

dll文件和so文件都是动态链接库,也叫共享对象文件

动态链接原理

动态链接库就是将程序模块相互独立的分隔开来,形成独立的文件,不再将它们静态地链接到一起。简单而言就是对那些组成程序目标文件的链接,等到程序运行时才进行链接,也就是把链接的过程推迟到运行时才进行,这就是动态链接的基本思想

动态链接没有提前链接的过程,但一般文件都是动态和静态链接结合使用,也有生成可执行文件这一步。

比如我编译a.cpp为共享对象文件a.so,还有a.h

b.cpp为c.o,此时我运行生成可执行文件,生成时侦测到sayHello为动态链接的实现,所以就不执行链接(如果侦测到为静态文件,那么就还是老样子进行链接)

b可执行文件执行时,遇到sayHello,就去动态库so中找这个函数的实现位置,这个找的位置有几类,我们经常用的是一个环境变量,叫LD_LABRARY_PATH,假设这里找到了,找到之后就将a.so加载到内存,链接,进行执行。

c执行时再用到a.so,此时a.so以及被加载到了内存中,所以不用再重新加载了,直接链接就能使用了。

那也就说明了动态链接的其他特点:

动态运行b.cpp之前必须要有a.so和a.h才能运行,所需的动态库本地必须齐全。

动态链接库的存储位置以及名称

lib文件的名称,要和头文件相呼应,不然怎么找对应的lib呢。

 比如

cudnn.h对应libcudnn.so

cudnn_adv_infer.h对应libcudnn_adv_infer.so等等

每一个头文件都要有唯一的一个对应的so文件。

一般so文件会存在lib下,头文件存在include下

如cuda11.6

 

 可以看到

再比如我们c++原生的一些实现

头文件也是在include下,可以看到我们的老熟人头文件iostream等等

 

模块

静态链接中,整个程序最终只有一个可执行文件,它是一个不可以分割的整体,但是在动态链接下,一个程序被分成了若干个文件,有程序的主要部分,即可执行文件(Program1)和
程序所依赖的共享对象(Lib.so),很多时候把这些部分叫做模块,即动态链接下的可执行文件和共享对象都可以看做是程序的一个模块

当程序模块Program1.c被编译成Program1.o时,编译器还不不知道foobar函数的地址,当链接器将Program1.o链接成可执行文件时,这时候链接器必须确定Program1.o中所引用的foobar函数的性质。

  • 如果foobar是一个定义与其它静态目标模块中函数,那么链接器将会按照静态链接的规则,将Program1.o中的foobar地址引用重定位
  • 如果foobar是一个定义在某个动态共享对象中的函数,那么链接器就会将这个符号的引用标记为一个动态链接的符号,不对它进行地址重定位,把这个过程留到装载时再进行

程序如何找到用到的动态库以及查看用到的动态库

动态库的搜索顺序如下:

  • LD_PRELOAD环境变量指定库路径
  • -rpath链接时指定路径
  • LD_LIBRARY_PATH环境变量设置路径
  • /etc/ld.so.conf配置文件指定路径
  • 默认共享库路径,/usr/lib,lib

应该就是按顺序一个一个寻找,直到找到满足要求的实现的动态库为止。

程序是如何找到动态库的? | 守望的个人博客

http://www.lryc.cn/news/59625.html

相关文章:

  • K8S部署常见问题归纳
  • Redis高可用
  • Hyperledger Fabric 2.2版本环境搭建
  • macOS Monterey 12.6.5 (21G531) Boot ISO 原版可引导镜像
  • 【软件设计师13】数据库设计
  • SpringMVC的全注解开发
  • C# | 导出DataGridView中的数据到Excel、CSV、TXT
  • 新规拉开中国生成式AI“百团大战”序幕?
  • 日撸 Java 三百行day31
  • 在线绘制思维导图
  • 月薪20k的性能测试必备技能:发现性能瓶颈掌握性能调优
  • 3、Web前端学习规划:CSS - 学习规划系列文章
  • 城市轨道交通列车时刻表优化问题【最优题解】
  • 常年不卷,按时下班,工作能力强,同事求助知无不言,不扯皮,不拉帮结派,这样的职场清流竟然被裁掉了!...
  • 基于改进多目标灰狼优化算法的考虑V2G技术的风、光、荷、储微网多目标日前优化调度研究(Matlab代码实现)
  • Python 函数、文件与模块
  • 在Spring Boot微服务使用RedisTemplate操作Redis
  • 4月软件测试面试太难,吃透这份软件测试面试笔记后,成功跳槽涨薪30K
  • 人人拥有ChatGPT的时代来临了,这次微软很大方!
  • 【C++11】自动类型推导(Type Inference)
  • 拐点!智能座舱破局2023
  • SAP开发环境ABAP的搭建(客户端和服务器),Developer Key和AccessKey的绕过方法
  • VSCode的C/C++编译调试环境搭建(亲测有效)
  • 物理世界的互动之旅:Matter.js入门指南
  • 在线文章生成器-文章生成器在线生成
  • 第十四届蓝桥杯大赛软件赛省赛-试题 B---01 串的熵 解题思路+完整代码
  • 【Leetcode】消失的数字 [C语言实现]
  • SpringBoot接口 - 如何实现接口限流之单实例
  • 【花雕学AI】深度挖掘ChatGPT角色扮演的一个案例—CHARACTER play : 莎士比亚
  • 腾讯云物联网开发平台 LoRaWAN 透传接入 更新版