https://www.cnblogs.com/codingmengmeng/p/6046481.html
一、库的基础概念:
在windows平台和linux平台下都大量存在着库。本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。由于windows和linux的本质不同,因此二者库的二进制是不兼容的。通俗的说就是把这些常用函数的目标文件打包在一起,提供相应函数的接口,便于程序员使用。在使用函数时,只需要包对应的头文件即可。按照库的使用方式又可分为动态库和静态库,在不同平台下对应后缀也有所不同。
WINDOWS下:.dll 后缀为动态库,.lib 后缀为静态库; LINUX下:.so后缀为动态库,.a后缀为静态库。
二、静态库与静态链接
<1>静态库:
静态库可以简单的看成一组目标文件的集合,即很多目标文件经过压缩打包后形成的文件。比如在我们日常编程中,如果需要使用printf函数,就需要包stdio.h的库文件,使用strlen时,又需要包string.h的库文件,可是如果直接把对应函数源码编译后形成的.o文件直接提供给我们,将会对我们的管理和使用上造成极大不便,于是可以使用“ar”压缩程序将这些目标文件压缩在一起,形成libx.a静态库文件。
注:静态库命名格式:lib + “库名称”+ .a(后缀) 例:libadd.a就是一个叫add的静态库
<2>静态链接:
对于静态库,程序在编译链接时,将库的代码链接到可执行文件中,程序运行时不再需要静态库。在使用过程中只需要将库和我们的程序编译后的文件链接在一起就可形成一个可执行文件。
注:详细关于静态库及静态链接可参考《程序员的自我修养——链接、装载与库》第四章
通过一个例子来了解下如何将我们自己写的头文件和代码同时进行编译链接,最终生成可执行文件:
/main.c/
#include <stdio.h>
#include "add.h"
int main()
{
int ret = add(3, 4);
printf("3 + 4 = %d\n",ret);
return 0;
}
/add.c/
#include "add.h"
int add( int x, int y)
{
return x + y;
}
/add.h/
#pragma once
#include <stdio.h>
int add( int x, int y);
/Makefile/
main : main.c libadd.a
gcc main.c -L . -ladd -o main
//-L为指定路径 .为当前目录下 -l+库名字,编译器可在指定目录下自己寻找名为add的库文件
libadd.a :
gcc -c add.c -o add.o
//ar -rc将多个编译后的文件打包为一个静态库文件
ar -rc libadd.a add.o
.PHONY:clean
clean:
rm main libadd.a
<3>缺点:
1、内存和磁盘空间浪费:
静态链接方式对于计算机内存和磁盘的空间浪费十分严重。假如一个c语言的静态库大小为1MB,系统中有100个需要使用到该库文件,采用静态链接的话,就要浪费进100M的内存,若数量再大,那浪费的也就更多。例如下图:程序1和程序2都需要用到Lib.o,采用静态链接的话,那么物理内存中就会存放两份对应此文件的拷贝。
2、更新麻烦:
比如一个程序20个模块,每个模块只有1MB,那么每次更新任何一个模块,用户都得重新下载20M的程序。
三、动态库与动态链接
<1>动态库:
程序在运行时才去链接动态库的代码,多个程序共享库的代码。一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码。
注:动态库命名格式:lib + “库名称”+ .so(后缀) 例:libadd.so就是一个叫add的动态库
<2>动态链接:
由于静态链接具有浪费内存和模块更新困难等问题,提出了动态链接。基本实现思想是把程序按照模块拆分成各个相对独立部分,在程序运行时才将他们链接在一起形成一个完整的程序,而不是像静态链接那样把所有的程序模块都链接成一个单独的可执行文件。所以动态链接是将链接过程推迟到了运行时才进行。
同样,假如有程序1,程序2,和Lib.o三个文件,程序1和程序2在执行时都需要用到Lib.o文件,当运行程序1时,系统首先加载程序1,当发现需要Lib.o文件时,也同样加载到内存,再去加载程序2当发现也同样需要用到Lib.o文件时,则不需要重新加载Lib.o,只需要将程序2和Lib.o文件链接起来即可,内存中始终只存在一份Lib.o文件。
注:详细关于动态库及动态链接可参考《程序员的自我修养——链接、装载与库》第七章。
动态库和动态链接的例子依然使用上面的代码,输出结果也相同,唯一需要改变的就是Makefile文件。
/Makefile/
main : main.c libadd.so
gcc main.c -L . -ladd -o main
//-L为指定路径 .为当前目录下 -l+库名字,编译器可在指定目录下自己寻找名为add的库文件
libadd.so :
gcc -fPIC -shared add.c -o libadd.so
//-shared表示输出结果是共享库类型的 -fPIC表示使用地址无关代码奇数来生产输出文件
.PHONY:clean
clean:
rm main libadd.so
error while loading shared libraries: libmymath.so: cannot open shared object file: No such file or directory
- 链接器: 工作与链接阶段,-l 与-L
- 动态链接器: 工作于运行阶段,需要提供动态库所在的目录位置
解决方案: 可使用
ldd
命令查看.out ldd可以列出一个程序所需要得**动态链接库**
- 修改环境变量
LD_LIBRARY_PATH = 动态库所在目录
export
LD_LIBRARY_PATH=动态库所在目录
(仅在当前进程有效) 若需永久有效,写入终端配置文件~/.bashrc
2.拷贝文件法(拷贝到系统Lib) 不建议cp 动态库文件 /lib
- 修改配置文件(
~/etc/ld.so.conf
) 不建议a. 写入动态库所在绝对路径 b.生效
sudo ldconfig -v
<1>
当我们生成可执行文件后,可使用ldd命令查看该可执行文件所依靠的动态库。
<2>
提到windows和Linux下库文件的后缀不同,更根本的原因在于二者文件格式都不同。可以通过file一个动态库查看Linux下动态库的文件类型其实是ELF格式。ELF动态链接文件被称为动态共享对象(DSO,Dynamic Shared Objects),简称共享对象;在windows下,动态链接文件被称为动态链接库(Dynamic Linking Library),也就是.dll文件后缀的全称
<3>优点:
①毋庸置疑的就是节省内存; ②减少物理页面的换入换出; ③在升级某个模块时,理论上只需要将对应旧的目标文件覆盖掉即可。新版本的目标文件会被自动装载到内存中并且链接起来; ④程序在运行时可以动态的选择加载各种程序模块,实现程序的扩展。