阿龙的学习笔记---《程序员自我修养-链接、装载与库》读书笔记(三)

六、动态链接

  • 动态链接
    • 静态链接可以让模块单独开发测试,但是每个程序内部都需要包含所有的模块,占用空间很大;如果两个程序都需要同一个模块,那么静态链接会有两个重复的模块都在内存中;再者,程序中模块更新,程序就要重新链接发布。
    • 动态链接同样也是将程序分模块,但是不再静态链接在一起,而是在运行时才链接在一起。
    • 动态链接还有一个特点是:可以在运行时动态选择加载各种程序模块。动态链接还可以加强程序的兼容性,在两个机器上printf()不同,那么只需要加载不同的运行库,而不是编译两套程序。
  • 基本实现
    • 一个小例子: 将lib.c文件编译为共享对象文件(Linux下动态链接文件,windows下叫动态链接库),调用库中的foobar()函数。
      在这里插入图片描述
    • 编译过程如下:
      在这里插入图片描述
    • 我们可以看到,上面Lib.c独立编译链接成了Lib.so,而好像Program1.o在链接的时候也用到了Lib.so,这是怎么回事呢?是因为在这一步,普通的静态链接会将地址等重定位,而他需要确定foobar()是一个动态链接的函数(在Lib.so中定义了foobar的符号),而标记为一个动态链接符号,在运行前装载时再确定地址。
    • 动态链接so文件也是ELF文件,起始地址是0x00000000,是无效的,所以说明最终装载地址在编译时是不确定的。
  • 地址无关代码
    • 共享对象的装载地址,很早以前是固定的,很多弊端。现在是会被分配到不同地址。而可执行文件基本可以确定自己在进程虚拟空间的起始位置(这里不是很明白),因为可执行文件往往是第一个被加载的文件,它可以选择一个固定空闲的地址,比如 Linux下般都是0x08040000, Windows下一般都是0x0040000。
    • 为了能让共享对象在任意地址装载,可以装载时重定位,装载地址确定了之后,系统对程序中所有动态链接地址进行重定位。但是有一个问题:动态模块装载至虚拟空间后,重定位会修改指令部分,比如一些模块间的调用,而同一份指令不能被多个进程共享。(为啥???)
    • 解决办法之一是地址无关代码(PIC Position-independent Code)。基本想法就是把指令中那些需要被修改的部分分离出来,跟数据部分放在一起,这样指令部分就可以保持不变,而数据部分可以在每个进程中拥有一个副本。
    • 有几种地址引用,一种是内部的函数调用、数据访问等,这个编译时按照相对寻址,所以不用加载也可以确定。另一种是外部的模块间的函数调用、数据访问等,这就涉及到这些模块都加载到什么位置了,编译时是无法确定的,那么可以将地址信息剥离,放进数据部分,怎么做呢?把这些地址放进一个全局偏移表(Global Offset Table),指令中通过间接引用,执行表中对应位置。
    • GOT是放在数据部分的,在模块加载时呗修改,每个进程的有独立的副本,互不影响,则代码部分做法哦了地址无关
    • 地址无关代码的编译参数是 -fPIC。
    • 使用装载时重定位方式的共享对象比地址代码无关的共享对象,运行时要快一些,因为少了一个间接地址寻址的过程。
  • 延迟绑定
    • 动态链接机制下,可能包含大量的函数调用,但不一定都会用得到,但动态链接过程很费事儿。所以在函数第一次被用到的时候才进行绑定。
  • 动态链接过程
    • 动态链接器自举,自举就是自己加载自己。
    • 然后将可执行文件和链接器的符号表合并,形成全局符号表。然后寻找依赖的共享对象。然后一个个加载,首先打开相应so文件,读取ELF头和.dynamic段,将相应的代码段和数据段映射到进程空间。
    • 如果这个共享对象还依赖其他的共享对象,则再进行加载。本质上是一个图,可以使用广度优先搜索的方式进行加载。
    • 每个对象的符号表都会被合并进全局符号表。
    • 上述完成后,动态链接器会对每一个共享对象的重定位表,将每个需要重定位的位置进行修正。
  • 显式运行时链接
    • 支持动态链接的系统往往都支持一种更加灵活的模块加载方式,叫做显式运行时链接
      ( Explicit Run-time Linking)
      ,有时候也叫做运行时加载。也就是让程序自己在运行时控制加载指定的模块,并且可以在不需要该模块时将其卸载。0

七、Linux共享库的组织

  • 共享库与共享对象从文件结构上来讲没什么差别,Linux下就是ELF的共享对象。
  • 命名一般以libname.so.x.y.z 或者 libname-x-y-z.so 。x为主版本号,y为次版本号,z为发布版本号。主版本号之间不兼容,次版本兼容,增量升级。
  • 共享库的路径一般是 /lib - 系统, /usr/lib - 非系统开发, /usr/local/lib - 第三方。
  • 环境变量: LD LIBRARY PATH。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值