0%

动态链接库(DLL)及其工作原理

动态链接库

动态链接库(Dynamic Link Library 或者Dynamic-link Library,缩写为DLL),是微软公司在微软Windows操作系统中,实现共享函数库概念的一种方式。 这些库函数的扩展名是”.dll、.ocx(包含ActiveX控制的库)或者.drv(旧式的系统驱动程序)。linux下的动态链接库后缀是so,即(shared object),共享对象。

许多程序拥有相同的功能,如果程序都重复的包含这些代码,将浪费很多硬盘空间。

程序1.exe 、2.exe 、3.exe有一段代码是相同的,那么我们可以将橙色这段代码从这三个程序中提取出来,保存成一个独立的动态链接库,等到程序运行的时候再将它们加载到内存。

假设源程序每个都是100M,共享的这段是50M,那么之前占硬盘300M,使用动态连接后占200M。其实动态链接库不仅可以节约空间,还更加方便程序的升级和维护。

创建动态链接库

创建matc.c

1
2
3
4
int add(int a,int b)
{
return a+b;
}

math.h

1
int add(int a,int b);

将math.c编译成一个动态库:

1
gcc -shared -fPIC math.c -o libmath.so
  • -shared 选项是表明这是一个动态库
  • -fPIC 位置无关代码(后面会讲)
  • libmath.so是我们定义的名字

主程序main.c

1
2
3
4
5
6
7
#include<stdio.h>
#include"math.h"
int main()
{
printf("add(1,2) return %d",add(1,2));
return 0;
}

编译整个程序

1
gcc main.c -lmath -L. -o main
  • -l指定动态链接库math,math是libmath.so,省略了lib和so后缀
  • -L指定动态链接库所在的目录

还不能直接运行程序

会报错找不到动态链接库,这是因为linux默认回去系统路径下搜索动态库,我们使用环境变量,将当前目录添加到LD_LIBRARY_PATH环境变量中,这样操作系统就会先去我们指定的目录搜索,如果没有则会继续前往系统路径搜索。

1
export LD_LIBRARY_PATH="$(pwd)"

成功!!!!

动态链接区别于静态链接的一点是方便升级和维护,此时我们只需修改math.c重新编译libmath,即可更改程序功能,非常的方便。将math.c修改

1
2
3
4
5
int add(int a,int b)
{
printf("you have changed the DLL\n");
return a+b;
}

动态链接与静态链接的区别

静态链接将所有用到的库和模块合并成一个独立的可执行文件,这一过程需要修复各个模块的函数跳转地址(重定位),因为在链接之前那些跳转地址不过是一堆占位符而已。

对于 动态链接 ,链接过程发生在程序加载时,在我们运行一个依赖动态链接库的程序,操作系统会首先将程序的数据、代码、连同用到的动态链接库递归的加载到内存,每个动态链接库的加载地址都是不固定的,操作系统会根据当前地址空间的使用情况为他们动态的分配一段内存。当动态链接库固定之后,就要进行修复函数的跳转地址,

因为动态链接库位置是不固定的,所以我们不能直接修改代码段,如果这样的话我们需要在不同的进程中保存不同的副本,显然是不合理的。于是在数段预留了一片区域存放跳转地址,被称为GOT(全局偏移表),在调用函数时首先会查表,然后根据表中的地址跳转,got的地址在动态链接库加载的时候会被修改为真正的地址,每个进程中的got都不相同,但是got所占用的空间很小可以忽略。这种方式被称为PIC(地址无关代码)。

延迟绑定

在程序运行的时候库中的很多函数都没有被调用,操作系统为了进一步的降低开销,不直接加载所有的函数,而是用到哪个函数的时候在进行查表搜索。

搬运的视频内容