动态链接-(1)

学习程序员的自我修养,这是我刚刚工作的时候,带我的师傅,推荐我看的第一本书,从今天开始希望一周能够读完这本书,之前初略看过一遍。

1.什么是动态链接?

把程序的链接过程推迟到运行时的在进行,被称为动态链接。

动态链接的思想: 把程序按模块拆分成多个独立的模块,在程序运行时,才将他们链接成一个单独的可执行文件。

比如:可执行文件A 依赖lib.so,可执行文件B也依赖lib.so, 系统加载A,发现链接时,依赖lib.so,会加载lib.so。

system::  A -->lib.so。

如果系统执行 B

system:: A-->lib.so--B

由于lib.so已经被加载到内存,所以不需要再次加载,省空间。

动态链接相比静态链接的好处是什么?

 1.静态链接,浪费空间,因为需要将所有的静态库连接成一个文件,每次要将所有的程序链接起来,然后发布户。

2.动态链接的好处:解决了静态链接两个困难,空间浪费和更新困难。

 从本质上讲,普通可执行程序和动态链接库中,都包含指令和数据,这一点没有区别。

在使用动态链接库的情况下,程序本身分为程序主要模块和动态链接库。实际上它们都可以看作整个程序的一个模块。当我们提到程序的模块时,指动态链接库,其实可执行文件和动态链接库,都可以被称为模块。

2.动态链接程序运行时地址空间的分布

 对于静态链接的可执行文件来讲,整个进程只有一个文件要映射,那就是可执行文件。

对于动态链接来讲,除了可执行文件,还有依赖的动态链接库,以及动态链接器,都需要被映射到虚拟地址空间。 在系统执行可执行文件时,动态链接器先进行链接,其链接工作完成后,交给可执行文件执行。

共享对象的装载位置:

共享对象的最终装载地址,在编译时是不确定的,可基本执行程序基本可以确定自己在进程虚拟地址空间的位置。

3.装载时重定位。

    为了能够使共享对象在任意地址装载,在链接时不进行重定位,而是讲重定位推迟到装载后再完成。系统在装载完之后,会遍历模块中的重定位表,找到需要从定位的引用。

   装载时重定位,对数据段是有效的,因为不同进程有这不同的数据段副本,但是代码段就不行,因为多个进程共享一份代码段,不能够去修改共用的代码段。 所以装载时从定位存在的缺点是指令部分无法在多个进程之间共享。

   希望程序模块中共享指令部分在装载时,不需要因为装载地址而改变,所以基本的思想方法是,把指令中那些需要修改的部分分离出来,跟数据部分放在一起,这样指令部分就可以保持不变,而数据部分在每个进程中,拥有不一样的副本,这就叫做地址无关代码的技术。

   共享模块中的地址分为两部分:

按模块区分模块内部引用和模块外部引用,

按引用方式区分,为指令引用和数据引用。

3.1 模块内部的调用和跳转

      模块内部 调用函数和被调用函数之间相对位置是固定的,模块内部的跳转,都是基于相对地址的调用,或者基于寄存器的相对调用,这种指令是不需要重定位的。

3.2 模块内部的数据访问

模块的指令和数据段的相对位置是固定的,也是基于相对地址的调用。

3.3  模块间的数据访问

模块间的数据访问要复杂一些,因为模块间的数据访问目标地址,要等到装载时,才能决定。

ELF 的做法是在数据段里面,建立一个指向这些变量的指针数组,也被称为全局偏移量。

数据段中,建立一个指向这些变量的指针数组,也被称为全局偏移表(Gost)

将非本模块的全局变量放在Gost中,用4个字节表示全局变量实际的地址。 

(1) 编译时,模块生成Gost表,保存未定义在此模块全局变量。 Gost 变量与指令之间时相对偏移。

(2)链接器在链接时,为Gost中每一个变量赋上其实际地址。

(3)程序调用时,先通过内部偏移跳转到gost,然后再偏移访问到变量a的位置。

3.4 模块间的调用和跳转

  同样时将外部函数放在Gost表中,不过储存的时函数的地址。

总结:

指令与跳转数据
模块内部相对跳转与调用相对跳转与调用
模块外部间接跳转与调用间接访问

fpic 时不包含任何可重定位表的。

4.共享模块的全局变量问题

  当一个模块引用了一个定义在共享对象的全局变量时, 编译器有时无法确定,变量是定义在同一个模块其他目标文件,还是定义在另外一个共享对象中。 

无法判断全局变量是否跨模块。

可执行文件引用动态库中变量,并且赋值。

可执行文件在运行时,不会进行代码重定位,所以在bss段中创建一个数据副本。

共享库中,也有一个副本, 一个变量存在多个位置,肯定是不行的。

ELF 共享库在编译时,把所有的定义在模块内部的全局变量,默认的当做定义在其他模块的全局变量。 链接器再链接时,发现如果全局变量在某个执行文件中有副本,会将GOT 中该变量的地址,指向该副本。 那么在实际执行中,只有一个副本。

如果变量在共享模块中被初始化,链接器,会将这个初始值,复制到程序主模块的副本上。

Q&&A:

如果在一个lib.so库中定义一个全局变量G,A进程和B进程都进行了装载,而进程A和进程B 使用是同一个副本吗?

每个进程都有独立的副本,无法访问。

如果是一个进程的两个线程呢?

两个线程访问同一个进程地址空间。

如何做到两个进程能够访问同一个副本?两个线程访问不同的副本?

进程间的通信,访问全局变量。 线程间的通讯。

5. 数据段的地址无关性

 

 对于数据段来讲,每个进程都会有一个独立的副本,所以不用担心被进程所改变,所以可以在重定位时,来解决数据段中绝对地址的引用问题。

  代码也可以使用重定位的方式,但是他将不能被多个进程装载。

  

  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值