gcc编译构建流程-动态链接库
创建一个动态库
├─mymath
│ └─src
│ │ └─mymath.cpp
│ └─include
│ │ └─mymath.h
代码实现如下
// mymath.h
#pragma once
int add_v1(int, int);// mymath.cpp
int add_v1(int a, int b)
{return a + b;
}
编译成一个动态库
g++ -fPIC -c src/mymath.cpp -o mymath.o -Iinclude # 生成位置无关代码
g++ -shared -o libmymath.so mymath.o # 生成共享库
其他项目引入
假如我们有个项目引入了这个三方库
├─mymath
│ └─mymath.so
│ └─include
│ │ └─mymath.h
├─main.cpp
main.cpp的使用如下
// main.cpp
#include <iostream>
#include "mymath.h"int main()
{int v1 = add_v1(1,2);std::cout << v1 << std::endl;
}
我们进行编译链接
gcc -c main.cpp -o main.o -Imymath/include # 编译
gcc main.o -o main -Lmymath -llibmymath -lstdc++ # 链接动态库
动态库升级
如果后续我们的动态链接库升级后,比如对add_v1进行了优化,新增了一个特性函数sub_v1等
// mymath.h
#pragma once
int add_v1(int, int);
int sub_v1(int, int); // 新增功能函数// mymath.cpp
int add_once(int a, int b) {return a + b + 1;
}// add函数进行优化
int add_v1(int a, int b) {return add_once(a, b);
}
int sub_v1(int a, int b) {return a + b;
}
整个流程其实没有什么变化
g++ -fPIC -c src/mymath.cpp -o mymath.o -Iinclude # 生成位置无关代码
g++ -shared -o libmymath.so mymath.o # 生成共享库
使用这个动态库的项目只要更新一下so文件就可以了,非常的简单。但是直接覆盖会有兼容性的问题,例如旧版本的程序运行这个新库的时候就会直接报错,我们希望这两个版本可以同时保留,并且进行切换。一种最简单直接的方法就是提供版本号,例如老版本叫做libmymath.so.1
,新版本叫做libmymath.so.2
,这样新老程序就可以同时跑起来了。
g++ -fPIC -c src/mymath.cpp -o mymath.o -Iinclude # 生成位置无关代码
g++ -shared -o libmymath.so.2 mymath.o # 生成共享库
动态库命名管理
兼容行管理有一套命名规范
Real Name(真实名称)
- 格式:lib<库名>.so.<主版本号>.<次版本号>.<发布版本号>(如 libz.so.1.2.8)。
- 作用:包含实际的二进制代码,文件名中的版本号标识具体功能变更:
- 主版本号:接口不兼容的重大升级(如删除函数)。
- 次版本号:向后兼容的功能新增(如添加新函数)。
- 发布版本号:Bug 修复或优化,不影响兼容性。
Soname(共享对象名)
- 格式:lib<库名>.so.<主版本号>(如 libz.so.1)。
- 作用:编译时写入库文件头(通过 readelf -d libxxx.so | grep SONAME 查看)。程序运行时按此名称加载库(如 libz.so.1 指向 libz.so.1.2.8)。
Link Name(链接名)
- 格式:lib<库名>.so(如 libz.so)
- 作用:编译时通过 -l<库名> 链接(如 -lz 会查找 libz.so)。本质是一个指向 soname 的软链接(如 libz.so → libz.so.1)。
名称类型 | 格式 | 作用场景 | 管理工具 |
---|---|---|---|
Real Name | libxxx.so.X.Y.Z | 实际二进制文件 | 编译器生成 |
Soname | libxxx.so.X | 运行时加载 | ldconfig 维护 |
Link Name | libxxx.so | 编译时链接 | 手动创建软链接 |