静态库(静态链接库lib/a)和动态库(动态链接库dll/so)

方法库大体上可以分为两类:静态库和动态库(共享库)。

1. windows中静态库是以 .lib 为后缀的文件,动态库是以 .dll 为后缀的文件。

2. linux中静态库是以 .a 为后缀的文件,动态库是以 .so为后缀的文件。

静态链接:

  • 静态库 在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接。
  • 静态库可以简单看成是一组目标文件(.o .obj文件)的集合, 将若干个.o文件转换为静态库的过程,称之为打包. Linux下是使用ar工具, Windows下是使用lib.exe。
  • Linux下静态链接库的后缀是.libWindows下静态链接库的后缀是.a

动态链接:

  • 动态库在程序编译时并不会被连接到目标代码中,而是在程序运行时才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新。
  • 在Windows系统下的执行文件格式是PE(Portable Executable)格式,动态库需要一个DllMain函数做出初始化的入口,通常在导出函数的声明时需要有_declspec(dllexport)关键字。 跟exe有个main或者WinMain入口函数一样,DLL也有一个入口函数,就是DllMain。根据编写规范,Windows必须查找并执行DLL里的DllMain函数作为加载DLL的依据,它使得DLL得以保留在内存里。这个函数并不属于导出函数,而是DLL的内部函数。这意味着不能直接在应用工程中引用DllMain函数,DllMain是自动被调用的。
    对于动态链接库,DllMain是一个可选的入口函数。一个动态链接库不一定要有DllMain函数,比如仅仅包含资源信息的DLL是没有DllMain函数的。
  • Linux下gcc编译的执行文件默认是ELF格式,不需要初始化入口,亦不需要函数做特别的声明,编写比较方便。
    无需打包工具,直接使用编译器即可创建动态库。
  • Linux下动态链接库的后缀是.so;Windows下动态链接库的后缀是.dll

一、动态链接库创建和使用

1.创建hello.so动态库

#include <stdio.h>void hello(){	printf("hello world\n");}编译:gcc -fPIC -shared hello.c -o libhello.so

2.hello.h头文件

void hello();

3.链接动态库

#include <stdio.h>#include "hello.h" int main(){	printf("call hello()");	hello();}编译:gcc main.c -L. -lhello -o main

这里-L的选项是指定编译器在搜索动态库时搜索的路径,告诉编译器hello库的位置。”.”意思是当前路径.

3.编译成够后执行./main,会提示:

In function `main':
 
main.c:(.text+0x1d): undefined reference to `hello'
collect2: ld returned 1 exit status

这是因为在链接hello动态库时,编译器没有找到。
解决方法:

sudo cp libhello.so /usr/lib/

这样,再次执行就成功输入:
call hello()

二、静态库 创建和使用

文件有:main.c、hello.c、hello.h
1.编译静态库hello.o: 

gcc hello.c -o hello.o  #这里没有使用-shared

2.把目标文档归档

ar -r libhello.a hello.o  #这里的ar相当于tar的作用,将多个目标打包。

程序ar配合参数-r创建一个新库libhello.a,并将命令行中列出的文件打包入其中。这种方法,如果libhello.a已经存在,将会覆盖现在文件,否则将新创建。

3.链接静态库

gcc main.c -lhello -L. -static -o main

这里的-static选项是告诉编译器,hello是静态库。
或者:

gcc main.c libhello.a -L. -o main

这样就可以不用加-static

4.执行./main

输出:call hello()

区别:

可执行文件大小不一样

从前面也可以观察到,静态链接的可执行文件要比动态链接的可执行文件要大得多,因为它将需要用到的代码从二进制文件中“拷贝”了一份,而动态库仅仅是复制了一些重定位和符号表信息。

占用磁盘大小不一样

如果有多个可执行文件,那么静态库中的同一个函数的代码就会被复制多份,而动态库只有一份,因此使用静态库占用的磁盘空间相对比动态库要大。

扩展性与兼容性不一样

如果静态库中某个函数的实现变了,那么可执行文件必须重新编译,而对于动态链接生成的可执行文件,只需要更新动态库本身即可,不需要重新编译可执行文件。正因如此,使用动态库的程序方便升级和部署。

依赖不一样

静态链接的可执行文件不需要依赖其他的内容即可运行,而动态链接的可执行文件必须依赖动态库的存在。所以如果你在安装一些软件的时候,提示某个动态库不存在的时候也就不奇怪了。

即便如此,系统中一般存在一些大量公用的库,所以使用动态库并不会有什么问题。

复杂性不一样

相对来讲,动态库的处理要比静态库要复杂,例如,如何在运行时确定地址?多个进程如何共享一个动态库?当然,作为调用者我们不需要关注。另外动态库版本的管理也是一项技术活。这也不在本文的讨论范围。

加载速度不一样

由于静态库在链接时就和可执行文件在一块了,而动态库在加载或者运行时才链接,因此,对于同样的程序,静态链接的要比动态链接加载更快。所以选择静态库还是动态库是空间和时间的考量。但是通常来说,牺牲这点性能来换取程序在空间上的节省和部署的灵活性时值得的。再加上局部性原理,牺牲的性能并不多。

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注