linux系统:共享库概念以及创建步骤总结
1.1 静态库
- 静态库也称为归档文件,可以理解为一组常用的目标文件被存放在一起,然后打了一个包,在链接阶段,连接器LD会自动链接该库文件
- 静态库的名字格式:libname.a
- ar -r 库名字 目标文件1 目标文件2:将目标文件添加到库中,并且替换掉相同名字的目标文件
- ar -tv 库名字:查看库中所有目标文件的详细信息
- ar -d 库名字 目标文件:从库中删除目标文件这个模块
- 使用静态库:
- 把静态库放在链接器会搜索的标准目录中(如/usr/lib),然后执行编译命令的时候加上-l并且指定库名字,如下:
- gcc demo.c -lname
- 如果没有放在可以搜索到的目录,可以用-L加上目录名字
- gcc demo.c -lname -L目录名字
- 一个静态库中可以包含很多模块,但是链接器只会链接需要的模块
- 把静态库放在链接器会搜索的标准目录中(如/usr/lib),然后执行编译命令的时候加上-l并且指定库名字,如下:
- 特点:
- 移植方便:因为程序运行期间不用再关心库的问题,链接的时候已经做好了
- 浪费空间:所有需要的静态库中的目标文件会链接到可执行文件中,增加可执行文件的大小
1.2 动态库
- 特点:
- 目标模块被多个程序共享
- 目标模块不会被复制到可执行文件中
- 第一个程序运行时候,会加载目标模块的副本进入内存中,之后其它程序也可以在这片内存中使用该目标模块
- 每个进程共享目标模块的代码,但是不会共享变量
1.2.1 使用一个共享库
- 如果要使用一个共享库需要做两件事情:
- 链接阶段将共享库的名称嵌入到可执行文件中
- 运行程序时候,解析库名,找到对应库文件,如果不在内存就加载进内存
root@ubuntu:/lib# gcc -g -Wall -o prog prog.c libfoo.so
- 将程序和共享库链接起来就会把其库名字加入到可执行文件中,这个阶段称为静态链接阶段
- 但是这个时候执行
./prog
文件会报错,这个阶段称为动态链接阶段,因为动态链接器LD在标准路径(/lib/等)找不到库文件,因为库文件存在于当前目录- 解决办法:
LD_LIBRARY_PATH
环境变量也是LD搜索的范围,只需要执行的时候LD_LIBRARY_PATH=. ./prog
就不会报错了
- 解决办法:
1.2.2 共享库别名soname
-
我们可以给共享库起一个别名,然后嵌入到可执行文件中的名字就是别名soname,而不是真实库名,这是为了提供一个间接层,当库的版本太多的时候,起到很好的作用
-
创建一个共享库,起别名的方式,并和一个程序链接起来:
-
把共享库加载进内存的过程:
-
LDD 可执行文件:可以看到可执行文件中包含哪些库,显示格式library-name > 库路径
1.2.3 共享库命名规范
-
库文件命名:libname.so.major-id.minor-id.补丁号
- major-id:主要版本号,表示不同的库版本,修改库某些东西之后,不在相互兼容
- minor-id:次要版本号,如果主要版本号相同,次要版本号不同,表示相互兼容,只是更新了一些东西
-
soname命名:libname.so.major-id
-
连接器名称:libname.so
-
这三个名称最好都放在同一个目录下面,这是规范
-
然后我们来想想,为啥需要soname和链接器名称,如果只用库的真实名称,不同库版本,我们都需要在链接的时候把库名字加在编译命令后面,这不是麻烦吗?所以我们用soname一直指向库的最新次要版本,然后我们在用一个链接器名称指向soname,这个时候编译的时候,我们就可以加上-lname选择,然后编译器自己会在相应的目录中的去寻找库文件,把soname嵌入到可执行文件中。同时如果就算出现了新的次要版本库文件,我们可以通过使用ldconfig命令来更新,然后就会自动让soname指向最新的次要库文件了,但是出现了新的主要版本库,需要认为改变链接器名称的指向
1.3 安装共享库
-
正规情况我们应该把库放在标准目录下:
-
ldconfig会先后搜索/etc/ld.so.conf中指定的目录、/lib、/usr/lib,然后为每个最新的库文件创建soname或者更新soname,最后在生成一个/etc/ld.so.cache缓存,其中保存了每个库的主要版本的最新次要版本,ldconfig -p可以查看/etc/ld.so.cache文件
-
ldconfig -nv .:处理当前文件下的库文件,不需要更新缓存文件/etc/ld.so.cache
-
程序运行期间我们也可以更新库文件,通过ldconfig刷新之后,如果更新的是主版本,我们需要重新为链接器名称指定最新的soname,然后程序终止或者重启的时候会调用新库
-
动态连接器查找库文件的步骤:
-
如果系统上存在libname.so和libname.a,默认会优先链接动态库libname.so,如果非要链接静态版本,就需要加上-static命令
-
大家可能会好奇,标准C库在链接的时候,可以不用加上-lname命令了,因为标准C库按照一定的规则,会让编译器和连接器自动去查找,而三方库和我们自己写的库,则需要我们加上-lname选择