操作系统系列八 ——动态链接(结合装载一起看)

往期地址:

  • 操作系统系列一 —— 操作系统概述
  • 操作系统系列二 —— 进程
  • 操作系统系列三 —— 编译与链接关系
  • 操作系统系列四 —— 栈与函数调用关系
  • 操作系统系列五 —— 目标文件详解
  • 操作系统系列六 —— 详细解释【静态链接】
  • 操作系统系列七 —— 装载

本期主题:
动态链接内容详解


动态链接

  • 0.前言——为什么需要动态链接
  • 1.动态链接例子
  • 2.地址无关代码
    • 2.1 基址重定位
    • 2.2 地址无关代码


0.前言——为什么需要动态链接

静态链接的原理简单也比较容易实现,还需要使用动态链接的原因是静态链接在大程序中会带来 内存的浪费程序发布麻烦 两大问题。

1.内存空间的浪费
可以想象在一个大程序中,肯定会有很多共同使用的函数,例如printf()、scanf()这种函数。
例如 program1、program2程序分别包含了 program1.o、program2.o两个模块,这两个模块都使用了 lib.o模块,在静态链接情况下,如果同时运行program1、program2,那么内存中就会有lib.o模块的两份副本。如果都是用静态链接的方式,那可想而知对整个空间的浪费会十分严重。

2.程序发布麻烦
比如程序program1中所使用的lib.o是由其他厂商提供的,一旦厂商更新了lib.o,如果是静态链接,整个program1就需要全部重新链接,再发布给客户。假如program1这个程序有20个模块,那么其中任何一个模块的更新都需要重新链接,这样会导致更新十分麻烦。

因此人们就在考虑如何才能解决上述的两个问题,其实解决这两个问题最简单的方法就是将程序模块独立开,不再静态链接在一起,简单来说,就是在程序运行的时候才对目标文件进行链接。 这也是动态链接和静态链接的最大差异

1.动态链接例子

动态链接的基本思想就是把程序按照模块拆分成各个相对独立的模块,在程序运行时将这些模块链接在一起。
其中,

在linux操作系统下,ELF动态链接文件一般是动态共享文件,一般以 .so为文件后缀;
在windows操作系统下,动态链接文件被称为动态链接库,以 .dll为后缀的文件;

举一个例子:
程序program1和程序program2都使用自定义的动态库,我们看下具体情况:

//program1
#include "my_lib.h"

int main(void)
{
    printf("Prgram 1\r\n");
    test_lib(1);

    return 0;
}

//program2
#include "my_lib.h"

int main(void)
{
    printf("Prgram 2\r\n");
    test_lib(2);

    return 0;
}

//my_lib.c
#include "my_lib.h"

void test_lib(int i)
{
    printf("Print in lib.so, %d\r\n", i);
}


$ gcc -fPIC -shared -o my_lib.so my_lib.c       生成动态共享文件
$ gcc -o program1 program1.c ./my_lib.so (注意这里的my_lib.so需要添加前面的./路径,不加的话会报错,怀疑是不加就是默认的/lib目录下找.so文件了)
$ ./program1 
Prgram 1
Print in lib.so, 1

可以查看进程的虚拟空间分布

jason@ubuntu:~/WorkSpace/3.OS_study/4.dynamic_link$ ./program1 &
[1] 30510
jason@ubuntu:~/WorkSpace/3.OS_study/4.dynamic_link$ Prgram 1
Print in lib.so, 1

进程的虚拟空间分布:
操作系统系列八 ——动态链接(结合装载一起看)_第1张图片
共享对象的装载地址:
操作系统系列八 ——动态链接(结合装载一起看)_第2张图片
从上面两张图能看出来,共享对象的装载地址和最终进程里的空间分布不能对应起来,因此可以推断 共享对象的装载地址在编译时是不确定的。

2.地址无关代码

2.1 基址重定位

前面提到的静态链接中的重定位属于链接时重定位,现在介绍动态链接中的一种重定位——基址重定位,这种重定位整个程序是按照一个整体被加载的,指令和数据的相对位置不会发生变化。
linux和GCC支持这种方式,使用的是 gcc 的 “-shared” 参数

2.2 地址无关代码

gcc的"-fPIC"参数,将程序模块的指令中那些需要被修改的部分给分离出来,然后跟数据部分放在一起,这样指令部分可以保持不变,而数据部分可以在每个进程中拥有一个副本,称为地址无关技术(PIC)

你可能感兴趣的:(计算机操作系统,操作系统)