在解释静态库和动态库之前,需要简单了解一下什么是目标文件。目标文件常常按照特定格式来组织,在linux下,它是ELF格式(Executable Linkable Format,可执行可链接格式),而在windows下是PE(Portable Executable,可移植可执行)。
而通常目标文件有三种形式:
我们来看一个简单实例:
//main.c
#include
#include
int main(int argc,char *argv[])
{
printf("hello world\n");
int b = 2;
double a = exp(b);
printf("%lf\n",a);
return 0;
}
代码计算e的2次方并打印结果。由于代码中用到了exp函数,它位于数学库libm.so或者libm.a中,因此编译时需要加上-lm。
前面所提到可重定位目标文件以一种特定的方式打包成一个单独的文件,并且在链接生成可执行文件时,从这个单独的文件中“拷贝”它自己需要的内容到最终的可执行文件中。这个单独的文件,称为静态库。linux中通常以.a(archive)为后缀
还是拿前面的例子来说,我们使用静态链接构建我们的可执行文件:
$ gcc -c main.c
$ gcc -static -o main main.o -lm
在这个过程中,就会用到系统中的静态库libm.a。这个过程做了什么呢?首先第一条命令会将main.c编译成可重定位目标文件main.o,第二条命令的static参数,告诉链接器应该使用静态链接,-lm参数表明链接libm.a这个库(类似的,如果要链接libxxx.a,使用-lxxx即可)。由于main.c中使用了libm.a中的exp函数,因此链接时,会将libm.a中需要的代码“拷贝”到最终的可执行文件main中。
我们看看最终生成的文件大小:
$ ls -lh main
-rwxrwxr-x 1 www www 988K 6月 27 20:22 main
生成的可执行文件大小为988k,由于最终生成的可执行文件中已经包含了exp相关的二进制代码,因此这个可执行文件在一个没有libm.a的linux系统中也能正常运行。
动态库和静态库类似,但是它并不在链接时将需要的二进制代码都“拷贝”到可执行文件中,而是仅仅“拷贝”一些重定位和符号表信息,这些信息可以在程序运行时完成真正的链接过程。linux中通常以.so(shared object)作为后缀。
通常我们编译的程序默认就是实用动态链接:
$ gcc -o main main.c -lm #默认使用的是动态链接
我们来看最终生成的文件大小:
$ ls -lh main
-rwxrwxr-x 1 www www 8.5K 6月 27 20:25 main
可以看到,通过动态链接的程序只有8.5k!
另外我们还可以通过ldd命令来观察可执行文件链接了哪些动态库:
$ ldd main
linux-vdso.so.1 => (0x00007ffc7b5a2000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fe9642bf000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe963ef5000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe9645c8000)
到这里我们大致了解了静态库和动态库的区别了,静态库被使用目标代码最终和可执行文件在一起(它只会有自己用到的),而动态库与它相反,它的目标代码在运行时或者加载时链接。正是由于这个区别,会导致下面所介绍的这些区别。
1.可执行文件大小不一样
从前面也可以观察到,静态链接的可执行文件要比动态链接的可执行文件要大得多,因为它将需要用到的代码从二进制文件中“拷贝”了一份,而动态库仅仅是复制了一些重定位和符号表信息。
2.占用磁盘大小不一样
如果有多个可执行文件,那么静态库中的同一个函数的代码就会被复制多份,而动态库只有一份,因此使用静态库占用的磁盘空间相对比动态库要大。
3.扩展性与兼容性不一样
如果静态库中某个函数的实现变了,那么可执行文件必须重新编译,而对于动态链接生成的可执行文件,只需要更新动态库本身即可,不需要重新编译可执行文件。正因如此,使用动态库的程序方便升级和部署。
4.依赖不一样
静态链接的可执行文件不需要依赖其他的内容即可运行,而动态链接的可执行文件必须依赖动态库的存在。所以如果你在安装一些软件的时候,提示某个动态库不存在的时候也就不奇怪了。
即便如此,系统中一班存在一些大量公用的库,所以使用动态库并不会有什么问题。
5.复杂性不一样
相对来讲,动态库的处理要比静态库要复杂,例如,如何在运行时确定地址?多个进程如何共享一个动态库?当然,作为调用者我们不需要关注。另外动态库版本的管理也是一项技术活。这也不在本文的讨论范围。
6.加载速度不一样
由于静态库在链接时就和可执行文件在一块了,而动态库在加载或者运行时才链接,因此,对于同样的程序,静态链接的要比动态链接加载更快。所以选择静态库还是动态库是空间和时间的考量。但是通常来说,牺牲这点性能来换取程序在空间上的节省和部署的灵活性时值得的。再加上局部性原理,牺牲的性能并不多。