静态链接库、动态链接库、导入库的区别

一、前言

通常情况下,对函数库的链接是放在编译时期(compile time)完成的。所有相关的对象文件 (object file)与牵涉到的函数库(library)被链接合成一个可执行文件 (executable file)。程序 在运行 时,与函数库再无瓜葛,因为所有需要的函数已拷贝到自己门下。所以这些函数库被成为静态库(static libaray),通常文件 名为“libxxx.a”的形式。其实,我们也可以把对一些库函数的链接载入推迟到程序运行的时期(runtime)。这就是如雷贯耳的动态链接库(dynamic link library)技术。

二、动态链接

动态链接方法:LoadLibrary()/GetProcessAddress()和FreeLibrary(),使用这种方式的程序并不在一开始就完成动态链接,而是直到真正调用动态库代码时,载入程序才计算(被调用的那部分)动态代码的逻辑地址,然后等到某个时候,程序又需要调用另外某块动态代码时,载入程序又去计算这部分代码的逻辑地址,所以,这种方式使程序初始化时间较短,但运行期间的性能比不上静态链接的程序。

三、静态链接

静态链接方法:#pragma comment(lib, "test.lib") ,静态链接的时候,载入代码就会把程序会用到的动态代码或动态代码的地址确定下来
静态库的链接可以使用静态链接,动态链接库也可以使用这种方法链接导入库

四、静态库和动态库的区别

在软件开发的过程中,大家经常会或多或少的使用别人编写的或者系统提供的动态库或静态库,但是究竟是使用静态库还是动态库呢?他们的适用条件是什么呢?

简单的说,静态库和应用程序编译在一起,在任何情况下都能运行,而动态库是动态链接,顾名思义就是在应用程序启动的时候才会链接,所以,当用户的系统上没有该动态库时,应用程序就会运行失败。再看它们的特点:

动态库:

  • 类库的名字一般是 libxxx.so
  • 共享:多个应用程序可以使用同一个动态库,启动多个应用程序的时候,只需要将动态库加载到内存一次即可;
  • 开发模块好:要求设计者对功能划分的比较好。
  • 动态函数库的改变并不影响你的程序,所以动态函数库的升级比较方便。

静态库:

  • 类库的名字一般是libxxx.a
  • 代码的装载速度快,执行速度也比较快,因为编译时它只会把你需要的那部分链接进去。
  • 应用程序相对比较大,如果多个应用程序使用的话,会被装载多次,浪费内存。
  • 如果静态函数库改变了,那么你的程序必须重新编译。

如果你的系统上有多个应用程序都使用该库的话,就把它编译成动态库,这样虽然刚启动的时候加载比较慢,但是多任务的时候会比较节省内存;如果你的系统上只有一到两个应用使用该库,并且使用的API比较少的话,就编译成静态库吧,一般的静态库还可以进行裁剪编译,这样应用程序可能会比较大,但是启动的速度会大大提高。

五、例子详解

文件目录树如下:

  1. libtest/
  2. |-- myjob.c
  3. |-- myjob.h
  4. |-- test.c

静态库

A.做成静态库 libmyjob.a

$ gcc -c myjob.c -o myjob.o 
$ ar -c -r -s libmyjob.a myjob.o

B.链接

$ gcc test.o libmyjob.a -o test 

C.引用库情况(无所要信息)

$ ldd test 
linux-gate.so.1 => (0xffffe000) 
libc.so.6 => /lib/libc.so.6 (0xb7e29000) 
/lib/ld-linux.so.2 (0xb7f6e000) 

动态库

A.做成动态库libmyjob.so

$ gcc -Wall –fPIC -c myjob.c -o myjob.o   
$ gcc -shared -o libmyjob.so myjob.o   
  • -shared: 该选项指定生成动态连接库(让连接器生成T >类型的导出符号表,有时候也生成弱连接 W >类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件。
  • -fPIC: 表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动>>态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的>。
  • -L.: 表示要连接的库在当前目录中。
    LD_LIBRARY_PATH: 这个环境变量指示动态连接器可以装载动态库的路径。

B.链接
链接方法一、拷贝到系统库里再链接,让 gcc 自己查找:

$ cp libmyjob.so /usr/lib 
$ gcc -o test test.o -lmyjob 

这里我们可以看到了 -lmyjob 选项, -l[lib_name] 指定库名,他会主动搜索。 lib[lib_name].so 这个搜索的路径可以通过 gcc --print-search-dirs 来查找。

链接方法二 ,手动指定库路径

$  gcc -o test test.o -lmyjob -B /path/to/lib

-B 选项就添加 /path/to/lib 到 gcc 搜索的路径之中。这样链接没有问题但是方法 II 中手动链接好的程序在 执行 时候仍旧需要指定库路径( 链接和执行是分开的 )。需要添加系统变量 LD_LIBRARY_PATH :

   1. $ export LD_LIBRARY_PATH=/path/to/lib 
这个时候再来检测一下test 程序的库链接状况 ( 方法 I 情况 )
   1. $ ldd test 
   2. linux-gate.so.1 => (0xffffe000) 
   3. libmyjob.so => /usr/lib/ libmyjob .so (0xb7f58000) 
   4. libc.so.6 => /lib/libc.so.6 (0xb7e28000) 
   5. /lib/ld-linux.so.2 (0xb7f6f000) 

是不是比静态链接的程序多了一个 libmyjob.so? 这就是静态与动态的最大区别,静态情况下,它把库直接加载到程序里,而在动态链接的时候,它只是保留接口,将动态库与程序代码独立。这样就可以提高代码的可复用度,和降低程序的耦合度。
另外,运行时,要保证主程序能找到动态库,所以动态库一般发布到系统目录中,要么就在跟主程序相对很固定的路径里,这样不管主程序在本机何时何地跑,都能找得到动态库。而静态库只作用于链接时,运行主程序时静态库文件没存在意义了。

推荐阅读更多精彩内容