Java基础与提高Linux

动态连接和静态连接的区别

2017-09-28  本文已影响3487人  lwj_ow

开始上课了,现在非周末的时间会稍微少点,这次想写的是关于静态链接和动态链接的区别,刚开始接触的时候还是有一点点的疑惑滴,这里比较一下来加深记忆.

  1. 我们在前面的博客里面写过关于.c文件从编译到链接然后到生成可执行文件的过程.通常情况下,对函数库的链接是放在编译期完成的.所有相关的对象文件与涉及到的函数库被链接为一个可执行文件.程序在运行的时候,与函数库再无瓜葛,因为所有我们需要的函数都已经被放在我们的可执行文件之中了.所以这些函数库也被称作静态库(static libaray),通常为libxxx.a(linux下).在Windows下是.lib文件

  2. 很显然,这种方式会使可执行文件变的很大,如果引入了很多的头文件,那么我们的可执行文件的大小可能会远远超过我们的预期.另外一个更严重的问题是一些常用的头文件基本上每个程序都会用到,所以这就导致了部分头文件被拷贝很多此,导致了无谓的内存浪费.因此,动态链接库就应运而生了.这个技术运行我们将一些库函数的链接载入推迟到程序运行的时候.
    使用动态链接的程序并不在一开始的时候就完成动态链接,而是真正到了调用动态库代码的时候,载入程序才计算(被调用的那部分)动态代码的逻辑地址,等到其他需要调用动态库的时候,程序又开始计算那部分代码的逻辑地址.
    因此,动态链接相对于静态链接的区别也很明显,动态链接的程序初始化速度比较块,但是运行期的速度又会慢一点.
    linux下动态库的名字一般为libxxx.so,Windows下动态库是.dll文件

  3. 动态库与静态库的区别

    • 简单来说,动态库总是与应用程序编译在一起的,在任何情况下都能运行,不依赖外部的情况.而动态库是动态连接的,顾名思义就是在应用程序真正运行的时候才会链接.所以,当用户的系统上没有该动态库时,应用程序就会运行失败.(在Windows下就是缺少dll文件).
    • 多个程序可以共享一个动态库,当启动多个使用相同动态库的应用程序时,只需要将动态库加载到内存一次就OK了.相反的,对于使用静态连接的应用程序一般都比较大,如果多个应用程序使用了相同的静态库,我们依然需要多次装载到内存中,浪费内存.
    • 静态连接的应用程序一般执行速度比较快,因为所有需要的代码都已经放在可执行文件之中了,而动态连接的应用程序却需要在运行过程中动态调用动态库,因此速度也略慢.
    • 对于静态连接的应用程序,如果你的静态库发生了变化,那么整个程序都会需要重新编译.而动态连接的应用程序就没这个问题了,在保持接口不变的情况下,我们升级动态库不影响我们的应用程序,所以在这一点上动态库是格外的方便.
  4. 下面我们来简单介绍一些静态库和动态库的用法.
    先写两个文件:

//test.c

include <stdio.h>

include <stdlib.h>

int sum(int a, int b);
int main(void)
{
printf("the sum of a and b is:%d\n",sum(a+b));
exit(0);
}
```

```
//sum.c
int sum(int a, int b)
{
    return (a+b);
}
```

上面是一个十分简单的例子,如果不使用静态库的话我们可以直接链接两个.c编译生成的.o文件就可以了,但是如果我们要用静态库的话,我们需要做以下这些操作:
> gcc -c sum.c -o sum.o
> ar -r libsum.a sum.o

然后我们就可以看到我们的当前目录下会产生libsum.a文件,这个就是我们的静态库,接下来就是连接静态库的过程了.
> gcc -o test test.c libsum.a

这个时候我们就会得到可执行文件test了,这个时候我们为了测试,删除了libsum.a文件,然后再执行test文件,发现仍然可以正常工作,说明我们的静态库确实是被编译进可执行文件了.为了比较大小的变化,我们再看看可执行文件的大小.
![image.png](https://img.haomeiwen.com/i5550522/69a12eb71f211388.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
可以看到大小是8712字节.再看看动态链接的情况:
![image.png](https://img.haomeiwen.com/i5550522/35018c6d7fc0ffc8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

下面我们再来试试动态库连接.
> gcc -fPIC -c sum.c -o sum.o
> gcc -shared -o libsum.so sum.o

* -shared:这个选项是用于生成动态连接库
* -fPIC:这个表示编译生成位置无光的代码,不使用该选项的话编译的结果仍然是位置相关的,也就是说动态载入的时候还是通过代码拷贝的方式来满足不同进程的要求,就失去了我们的代码共享的目的.
接下来是链接的过程:
> cp libsum.so /usr/lib(可能需要超级用户权限)
> gcc -o test test.c -lsum

对于链接的方式还有一种是自己手动指定库路径,不过要麻烦一点,因为我们每次都需要手动指定路径,而且执行可执行文件的时候也需要指定路径,并不支持大家用这个方法.
接下来我们可以看看大小,理论上动态连接的应用程序应该是远小于静态连接的程序的,但是在我们的测试中,由于sum.c文件很小,所以可能差距不是很大,但是可以肯定的是动态连接的程序是肯定要更小.的.
![image.png](https://img.haomeiwen.com/i5550522/4b1d58c84913a395.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
可以看到大小是8688字节,稍微小一点的(笑),这个对比确实不是很明显.下面我们来看看链接的情况:
![image.png](https://img.haomeiwen.com/i5550522/68cf625412c98549.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
这个图片里面我可以很明显的看到我们的libsum.so动态库是被动态链接的,如果我们去删掉/usr/lib里面的libsum.so,再运行test肯定是会失败的.

结语:说到这里差不多就快说完了,一般来说我们在使用Windows的时候还是很经常能看到dll文件的,这就是在Windows平台下的动态连接库,一般软件发布的时候这些文件的位置都是相对固定的,如果你随意移动这些文件的话,可能应用程序就无法运行了,这也算是动态连接库的一个小缺点,不过瑕不掩瑜,动态库的优点还是很多的,所以一般来说动态库可能用的也更多.

上一篇下一篇

猜你喜欢

热点阅读