动态库
1.命名规则
Linux: libxxx.so
lib
:前缀(固定的);xxx
:动态库的名字(自己取);.so
:后缀(固定的);
Windows:libxxx.dll
2.动态库的制作
- 使用
gcc
得到.o
文件 , 得到和位置无关的代码:
gcc -c -fpic a.c b.c ...
- 使用
gcc
得到动态库:
gcc -shared a.o b.o ... -o libxxx.so
3.示例
我们先新建如下结构的 demo :
以上代码在博客 : Linux生成静态库
1.使用 gcc
得到 .o
文件 , 得到和位置无关的代码
此时报错显示 找不到 head.h
。是因为我们没有指定头文件的路径,在命令后加上 -I ../include/
就可以了。
2. 使用 gcc
得到动态库
将生成的动态库文件 libcal.so
移动到 lib/
目录下。
3.生成可执行程序 app
并执行
当我们生成可执行文件 app
并执行后,报错了。
它显示找不到这个动态库。
执行失败的原因需要先了解 linux
加载动态库的流程和原理。
4.工作原理
- 静态库:
gcc
在进行链接时,会把静态库中的代码打包到可执行文件中; - 动态库:
gcc
在进行链接时,动态库的代码不会被打包到可执行文件中;
在程序启动之后,动态库会被动态加载到内存中,通过 ldd
(list dynamic dependencies)命令检查动态库的依赖关系。
那么我们要如何定位共享文件呢?
当系统加载可执行文件的时候,能够知道其所依赖库的名字,但是还要知道这个库的绝对路径。此时就需要系统动态的载入器来获取该库的绝对路径。
对于 elf
格式的可执行文件,是由 ld-linux.so
来完成的。它先后搜索 elf
文件的 DT_RPATH
再到 环境变量LD_LIBRAR_PATH
再到 /etc/ld.so.cache
文件列表 再到 /lib/ , /usr/lib
目录,找到库文件之后将其加载到内存中。
针对示例代码中的报错,我们先用 ldd
检测依赖关系:
我们发现系统显示 找不到 libcal.so
文件。
由于 DT_RPATH
一般是不能改动的,所以我们可以先修改环境变量 LD_LIBRARY_PATH
。
我们先获得动态库libcal.so
的绝对路径。
1.修改环境变量 LD_LIBRARY_PATH
,指对当前终端有效
此时添加了环境变量之后,app
就可以正常执行了。不过这个设置只对当前终端有效,是临时性的。
我关闭这个终端,再新开一个终端,之前的设置都无效了。
2.修改环境变量 LD_LIBRARY_PATH
,用户级别
修改用户家目录下的 .bashrc
文件,即 ~/.bashrc
,添加上这一句命令 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:动态库文件的绝对路径
。
之后再刷新 .bashrc
文件。
source .bashrc
再执行。
此时再新开一个终端也没问题。
3.修改环境变量 LD_LIBRARY_PATH
,系统级别
在系统配置 /etc/profile
里面添加上那个命令 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:动态库文件的绝对路径
。
保存退出之后,刷新并执行。
4.修改 /etc/ld.so.cache
文件
由于 /etc/ld.so.cache
是一个二进制文件,无法修改。
我们可以通过修改 /etc/ld.so.conf
来达到间接修改的目的,我们直接将 libcal.so
的绝对路径添加到 /etc/ld.so.conf
即可。
接着更新并执行,注意更新的命令是 ldconfig
。
5.静态库和动态库
静态库 和 动态库的主要区别是 来自链接阶段如何处理、链接成可执行程序。分别称为静态链接方式 和 动态链接方式。
1.静态库的制作过程
2.动态库的制作流程
3.静态库与动态库的区别
静态库
优点:
- 静态库被打包到应用程序中加载速度比较快;
- 发布程序的时候不需要提供静态库,移植方便;
缺点:
- 更消耗系统资源,浪费内存,可执行文件体积过于臃肿;
- 更新、部署、发布非常麻烦;
动态库
优点:
- 可以进行进程间资源共享(共享库);
- 更新、部署、发布更为简单;
- 可以人为的控制何时加载动态库;
缺点:
- 加载到内存的速度比不上静态库被打包到程序中;
- 在发布程序时需要提供依赖的动态库;