Linux动态链接机制及原理

动态链接VS静态链接:

动态链接从命名中可以看出动态是关键, 那这个动态体现在哪呢?  静态链接中的静态体现在哪呢?, 我们知道代码需要经过几个步骤才会编译成机器认识的符号, 代码一般经过预编译, 编译, 汇编, 链接然后形成可执行程序或者动态库, 这几个步骤的作用分别如下:
预编译: 将代码进行整理,#include,#define,注释的代码等等...
编译:将预编译后的文件进行语法分析, 词法分析, 语义分析及优化等
汇编:变成机器可以执行的指令
链接:将目标文件及库一起链接成exe文件,链接过程主要包括地址和空间分配, 地址绑定, 重定位等步骤
我们看动态链接, 静态链接看定义上看, 就只到其却别在链接这个阶段, 一个是动态的, 一个是静态的, 所谓静态我们可以理解为在链接阶段, 需要把可执行文件所依赖的所有目标文件及库都链接进来, 然后进行地址的重定位,  链接后就是一个完整的可执行文件, 一个不可分割的整体, 可执行文件中包含所有的目标文件及库.
动态链接在链接阶段不进行链接, 等到程序运行时才进行链接, 也就是说 把链接这个过程推迟到了运行时进行.动态链接时, 链接器需要找到代码中相关函数,变量定制在哪个动态库中, 从动态的符号表中可以检索到对应函数, 变量, 当链接器知道先关函数及变量是定义在动态库中, 则会改成对先关函数, 变量的动态符号引用. 待运行时装载. 装载后进行重定位到对应进程的虚拟内存中.

静态库VS动态库:

静态库: 浪费内存,占用大量磁盘空间,  程序迭代不方便, 牵一发而动全身.
动态库: 没有静态库的以上缺点, 也即是它的优点,  缺点是动态库因为是运行时装载, 装载时重定位, 所以程序运行的时候会慢一点, 性能差一点, 一般在5%左右, 都可以接受.

动态链接的基本实现

动态链接的基本思想是把程序按照模块拆分成各个相对独立的部分, 在程序运行时才将他们链接在一起形成一个完整的程序,而不是像静态链接一样把所有的程序模块都链接成一个单独的可执行的文件. 
当程序被装载时, 也即在运行时, 系统的动态链接器会将程序所需要的所有动态链接库(除延迟加载外, 延迟加载也即只有在用到该库的时候才去装载)装载到进程的地址空间,并且将程序中所有未决议的符号绑定到相应的动态链接库中, 并进行重定位工作(重定位工作就是将动态库中的函数及变量, 根据地址偏移加载到进程的对应地址空间中)
动态库在运行时装载, 在编译的时候无法确定库的装载地址, 只有在运行时, 装载器会根据当前的地址空间的空闲情况, 动态分配一块足够大小的虚拟地址空间给动态库来装载.
-fPIC: 地址无关代码
	一般我们在编译动态库时, 都会制定两个参数如:gcc -fPIC -shared -o lib.so lib.c,  -fPIC标识地址无关代码, 此处的地址是指在进程中的地址, 无关代码指的是动态库中的指令部分, 也即非数据部分, 库包括指令和数据,  指令是可以共享的, 也即多个进程加载同一个动态库时, 指令部分是共享的, 在物理地址上是只有一份的, 但动态库中的数据部分是独立的, 每个进程都有一份副本, 每个进程独有, 进程之间数据部分互不影响, 这些数据部分一般存放在虚拟地址空间的数据段.
-shared 装载时重定位
	这个概念前面已经讲了很多次, 核心思想是: 在程序运行时, 当库的转载地址确定后, 才会去将编译期的所有地址符号引用(也即库中的函数, 变量等符号)重定位到程序的对应的地址空间中, 这部分是根据地址偏移来计算的, 由库的转载地址作为偏移的基地址.

引出问题:

Q:动态库中全局变量, 当多个进程加载同一个动态库时, 修改库中的全局变量, 对其它进程是否可见:
	答案:不可见, 解释见-fPIC解释
Q2 动态库中全局变量, 一个进程中, 修改全局变量,  线程间是否可见?
	答案: 可见, 库被进程加载, 在进程内可见, 线程当然可以可见
Q3 一个进程加载相同的动态库(不通版本)多次(不论是静态加载还是动态加载), 请问当调用动态库的对外接口时, 会调到哪个库接口中?
答案: 动态链接器中有一个进程级别的全局动态符号表, 当哪个库最先被加载(静态加载或者是动态加载)时, 其库中所有符号在全局动态符号表中记录, 下次再加载不同版本同名库时, 因为全局符号表中已存在相同的符号名已经存在, 则后加入的符号会被忽略.也即进程加载不同版本的同名动态库时, 调用的接口都是最先被加载动态库的接口, 后面加载的库的接口会被忽略, 因为全局符号表把后加入的符号给忽略了. 哪那个库最先被装载符号就最先被加入到全局符号表.

动态链接的步骤和实现

动态链接分为三步: 
	1:先是启动动态链接器本身
	2:然后装载所有需要的共享对象
	3:最后是重定位和初始化
装载共享对象:
	动态链接器将可执行文件和链接器本身的符号都合并到一个符号表中-全局符号表,  然后链接器根据符号引用找到对应的动态库, 每个动态库的结构中都有一个.dynamic段, 这个段里面保存了动态链接器所需要的基本信息, 比如依赖于哪些共享对象, 动态链接符号表的位置, 动态链接重定位表的位置, 共享对象初始化代码的地址等等,  从该段中找出所依赖的其它动态库, 依次链接器将可执行文件所依赖的所有动态库对象的名字放入到一个装载集合中, 然后链接器开始从集合中取一个所需要的动态库名字, 找到相应文件并打开, 读取相应的ELF文件头和dynamic段, 然后将他相应的代码段和数据段映射到进程空间中,如果这个ELF动态库对象还依赖其它动态库, 那么将所依赖的库放入装载集合中, 如此循环直到所有动态库都被装载完毕后, 将进行符号重定位和动态库初始化工作(.init段),  至此动态链接器工作完成,  返回用户空间, 将控制权交给程序入口并且开始执行.

Linux进程分布图

:
在这里插入图片描述

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值