2 动态库与静态库之函数篇
- 作用
分离编译
代码重用 - 分类
分类 | 作用 | 后缀 |
---|---|---|
静态库 | 一个或多个.o目标文件归档在一个文件中 | .a |
共享库 | 没有main 函数的可执行文件 | .so |
动态加载库 | 没有main 函数的可执行文件,接口复合API | .so |
2.1 静态库的制作与使用
2.1.1 素材(文件)
Func.cpp
#include <iostream>
minclude "Func.h"
using namespace std;
void Func(int n){
cout << __func__ << " " <<n << endl;
}
Func.h
#pragma once
void Func(int n);
main.cpp
#include <iostream>
#include "Func.h"
using namespace std;
int main(){
Func(100);
}
2.1.2 创建
(1)编译源文件
g++ -c -o Func.o Func.cpp
(2)生成静态库
ar -rcs libFunc.a Func.o
ar
选项
命令选项 | 作用 |
---|---|
r | 替换模块(replace) |
c | 创建库(create) |
s | 建立索引 |
注:
tar和ar都是归档工具
tar用于创建.tar归档文件。
ar用于创建归档文件,并且为归档的目标文件中的符号建立索引。
- 查看目标文件的符号(symbol)信息
nm 目标文件
目标文件可以是.o、.a,也可以是可执行文件。
2.1.3 使用
- 链接静态库
g++ -o main main.cpp -L. -lFunc
或者
g++ -o main main.cpp ./libtest.a
注意:库一定要放在命令行的末尾
- 测试
./main
- 结果、
Func 100
2.2 动态库的制作与使用
2.2.1 创建
(1)编译目标文件
g++ -c -fPIC Func.cpp -o Func.o
(2)生成动态库
g++ -shared Func.o -o libFunc.so
注:以上两步可以合并为g++ -shared -fPIC -o libFunc.so test.cpp
选项说明
命令选项 | 作用 |
---|---|
shared | 创建动态库 |
fPIC | 代码都是与位置无关的 |
每个共享函数库都有个特殊的名字,称作soname
。soname
名字命名必须以lib
作为前缀,然后是函数库的名字,然后是.so
,最后是版本号信息。不过有个特例,就是非常底层的C库函数都不是以lib
开头这样命名的。
2.2.2 使用
(1)生成可执行文件
g++ -o main main.cpp -L. -lFunc
或者
g++ -o main main.cpp ./libFunc.so
注意:库一定要放在命令行的末尾
(2)测试
指定动态链接库位置
export LD_LIBRARY_PATH=动态链接库位置
执行
./main
结果
Func 100
- 关于动态链接库的安装路径
如果安装在/lib
或者/usr/lib
下,那么ld
默认能够找到,无需其他操作。
如果安装在其他目录,需要将其添加到/etc/ld.so.cache
文件中,步骤如下:
编辑/etc/ld.so.conf
文件,加入库文件所在目录的路径
运行ldconfig
,该命令会重建/etc/ld.so.cache
文件
注:当静态库和动态库同名时, gcc命令将优先使用动态库。
- 查看执行文件链接的动态链接库:
ldd 可执行文件
[root@localhost Func2]# ldd main
linux-vdso.so.1 (0x00007fffb1da5000)
./libFunc.so (0x00007fa2aeb6c000)
libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007fa2ae7d7000)
libm.so.6 => /lib64/libm.so.6 (0x00007fa2ae455000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fa2ae23d000)
libc.so.6 => /lib64/libc.so.6 (0x00007fa2ade79000)
/lib64/ld-linux-x86-64.so.2 (0x00007fa2aed6e000)
2.3 动态加载库
2.3.1 修改main.cpp
maindl.cpp
#include <iostream>
#include <dlfcn.h>
using namespace std;
int main(){
void* handle = dlopen("./libFunc.so",RTLD_LAZY);
if(nullptr == handle) return 0;
typedef void (*Func_t)(int);
Func_t pFunc = reinterpret_cast<Func_t>(dlsym(handle,"_Z4Funci")); //在动态库中找到Func函数,强转之后采用函数指针接收
//Func_t pFunc = (Func_t)dlsym(handle,"Func"); //在动态库中找到Func函数,强转之后采用函数指针接收
pFunc(100);
dlclose(handle);
handle = nullptr;
}
2.3.2 使用
(1)编译
g++ maindl.cpp -ldl
(2)执行
[root@localhost Func2]# ./a.out
(3)结果
Func 100
2.4 动态库(共享库、动态加载库)与静态库的区别
2.4.1 区别
(1)静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库,因此体积较大。
(2)动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在,因此代码体积较小。
2.4.2 优缺点
- 动态加载
(1)灵活,可以在需要的时候进行加载,在不需要的时候进行卸载,这样可以不必占用内存。
(2)可以在没有动态库时候发现,而不致程序报错。
(3)加载程序中有条件才运行的库。
(4)热更新,在不停止程序的前提下进行更新。
(5)复杂一些,需要显示获得函数地址。 - 静态加载
(1)简单方便
(2)没有找到动态库时,系统报错
(3)加载运行很久的库
2.5 总结
- 静态库、共享库与动态库编译链接使用比较
- 静态库与动态库
Make file
比较
2.6 补充
使用动态库的方法还有如下几种
方法一:连接前,添加动态库目录到环境变量LD_RUN_PATH
。
export LD_RUN_PATH=动态库目录
方法二:编译链接时,添加链接选项-Wl,-rpath -Wl,动态库目录
方法三:执行前,添加动态库目录到环境变量LD_LIBRARY_PATH
。
export LD_LIBRARY_PATH=动态库目录
方法四:添加共享库目录/usr/local/lib
到共享库配置文件
echo 动态库目录 >> /etc/ld.so.conf
ldconfig