1、动态链接库
动态链接库:不会把代码编译到二进制文件中,而是运行时才去加载,所以只需要维护一个地址
- 动态:运行时才去加载,即所谓的动态加载
- 连接:指库文件和二进制程序分离,用某种特殊的手段维护二者之间的关系
- 库:Windows下为.dll库文件,Linux下为.so库文件
动态链接库文件的好处是程序和库文件分离,可以分别发行,库文件可以被多处共享
2、动态库的制作与使用
2.1、制作一个动态库
-
首先生成一个SoTest.h、SoTest.cpp分别存放类声明和定义
// 声明一个抽象类,因为有纯虚函数 class SoTest { public:void func1();virtual void func2();virtual void func3() = 0; };// 实现 #include "SoTest.h" #include <iostream>void SoTest::func1() {std::cout << "func1()" << std::endl; }void SoTest::func2() {std::cout << "func2()" << std::endl; }
-
执行编译命令生成一个动态链接库
g++ -shared -fPIC SoTest.cpp -o libSoTest.so
此时当前目录下会生成一个 libSoTest.so的文件表示动态库
- -shared指令:表示这是一个共享库
- -fPIC指令:PIC(Position Independent Code)地址无关代码,加入这个指令后生成的动态库不需要进行任何的修改被加载到任意内存地址都是能够正常运行的。(后面在展开说)
- 动态库的命名规则很奇葩,其实在
/user/local/lib
等Linux的文件夹下经常可以看到libxxx.so的文件- lib是一种大家默认的命名规范,但是最后加载的时候又会去掉这个lib,只能说很奇葩
- .so是表示这是一个动态库
- -l(小写L):指定动态库的文件的名称,名称需要去掉lib和.so,如上!
- -I(大写i):指定头文件目录,默认为当前目录
- -L:手动指定动态库库文件搜索目录,默认只链接共享目录(系统的lib动态库),运行的时候还需要指定一次
2.2、编写程序使用动态库
-
首先准备一个main.cpp引入SoTest.h头文件,对SoTest进行继承并且重写几个函数
#include <iostream> #include "SoTest.h"class Test: public SoTest{ public:void func2() override {std::cout << "Test::func2()" << std::endl;}void func3() override {std::cout << "Test::func3()" << std::endl;} };int main() {Test test;test.func1();test.func2();test.func3();std::cout << "Hello, World!" << std::endl;return 0; }
-
使用下面的指令进行编译
g++ main.cpp -o main -L ./ -l SoTest
2.3、运行时动态库的连接
- Linux默认动态库路径配置文件:
/etc/ld.so.conf /etc/ld.so.conf.d/*.conf/lib/* /usr/lib /usr/local/lib
...
上面的指令只是在编译时对这个动态库进行了链接,但是运行时是找不到的了,因此运行的时候会报错,报错原因:
- 链接器:工作于链接阶段,工作时需要-l和-L分别指定库名和动态库路径
- 动态链接器:工作与程序运行阶段,工作时需要提供动态库所在目录位置,而动态库加载,程序是默认从几个系统指定的位置加载这个libSoTest.so文件。
- 当前位置的加载,因此需要给给定一个路径使得系统能够加载到动态库,通过ldd main可以看到找不到这个库
2.4、解决方法
解决方法其实还是蛮多的,大概有3-4种的样子
- 方案一:
export LD_LIBRARY_PATH=./(动态库路径),这是临时环境变量,关闭bash就不会有了 - 方案二:
将动态库路径写入环境变量.bashrc作为永久环境变量(bash启动就有) - 方案三:
拷贝自定义动态库到系统/lib或者其他一些指定的系统加载lib下 - 方案四:
sudo vim /etc/ld.so.conf
写入绝对路径
sudo ldconfig -v 使得配置文件生效
不管上面那种写法,最后lld main都能看到对应的libSoTest.so的路径在哪
2.5、Makefile
main: libSoTest.so${CXX} main.cpp -o main -L ./ -lSoTestsudo cp libSoTest.so /lib/lld mainlibSoTest.so:${CXX} -shared -fPIC SoTest.cpp -o $@clean:${RM} *.so mainsudo rm -rf /lib/libSoTest.so