linux下有两种库,静态库和动态库,它们分别以 .a
和 .so
为后缀名,链接时指定静态库文件,编译器会将静态库的目标代码嵌入到可执行文件中,所以可执行文件的体积较大,共享库的代码是在程序运行时才载入内存的,而且可以实现多个程序共享,在编译过程中仅简单的引用,因此代码体积较小
另外,静态库是将目标代码嵌入到可执行程序,所以,后续库的升级需要重新编译可执行程序,而动态库则只需要更新库文件即可,不需要重新编译可执行程序,它的升级相对方便
下面分别介绍静态库和动态库的生成步骤和使用的方法
当前目录是 sotest, b.h 和 b.cpp 位于 sotest/lib 目录下
// sotest/lib/b.h
#include <stdint.h>
#include <string>
#include <iostream>
using namespace std;
const std::string &UserName();
const std::string &UserPwd();
// sotest/lib/b.cpp
#include "b.h"
std::string g_username = "mytest";
std::string g_userpwd = "654321";
const std::string &UserName()
{
return g_username;
}
const std::string &UserPwd()
{
return g_userpwd;
}
b.o
的目标文件b.o
目标文件打包成 libb.a
文件,libb.a
就是静态库文件关于
ar
命令见下方的说明命令格式:
ar rcs libxxx.a file1.o file2.o
表示生成一个名为libxxx.a
的静态库文件r - 插入目标文件到库中
c - 创建库文件
s - 创建库文件的索引
// sotest/btest.cpp
#include "lib/b.h"
int main(int argc, const char *argv[])
{
cout << UserName() << ", "<< UserPwd() << endl;
return 0;
}
使用下面的命令编译代码,编译成功之后执行 ./mytest , 结果如下
还是以上面 sotest/lib 目录下的 b.h 和 b.cpp 以及 sotest/btest.cpp 为例来说明动态库的生成和使用方法
进入 sotest/lib 目录,执行下面的编译命令,编译完成之后,sotest/lib 目录下会生成一个 libb.so 的动态库文件
编译说明:-fPIC 告诉编译器产生与位置无关代码,也就是说,产生的代码中,没有绝对地址,全部使用相对地址,所以代码可以被加载到内存的任意位置上,都可以正确的执行。这一点非常重要,也是动态库要求的
还是使用上面的测试代码,进入 sotest 目录,使用下面的命令编译,并运行,结果如下
命令说明:-L 表示指定动态库的路径,如果没有指定表示使用系统默认的搜索路径(一般是 /lib 或 /usr/lib ), -lb 表示引用 libb.so 库,引用的时候省略了前面的 lib 以及 .so 后缀
我们发现编译成功之后,直接运行可执行程序会报错:加载共享库出错,没有这个文件或者目录,这是由于动态链接器加载时找不到动态库
可能有同学会说,上面编译时,不是用 -L 指定了动态库的路径吗,为什么运行的时候还是提示找不到动态库呢?
这是其实是编译链接和运行加载的区别,上面编译时用 -L 指定的路径,仅指定了编译链接阶段的库文件搜索路径,它无法决定运行时加载库文件的搜索路径,要确保编译通过,只要在 -L 指定的路径中找到了库文件就可以了,但运行与具体的运行环境有关,在编译时无法确定运行时库的搜索路径
所以,运行时提示 libb.so 未找到,一般有下面几个原因
1、libb.so 库文件本身不存在,需要确保已生成该库文件
2、 库文件存在,但不在默认搜索路径中,需要将库文件的路径添加到环境变量 LD_LIBRARY_PATH 中,或者将库拷贝到标准路径 /lib 或 /usr/lib 下
3、库文件依赖其他库文件,需要确保递归地将所有依赖的库路径添加正确
4、 库文件存在且路径正确,但文件名不匹配,例如库文件名是 libb.so.1,而程序在找 libb.so
5、 权限问题,导致无法读取库文件
6、 库文件版本不兼容导致读取失败
逐条对比下就会发现,我们这里是由于没有设置动态库的路径,设置路径有下面几种方法
1、把 libb.so 拷贝到 /lib 或者 /usr/lib
执行 cp 命令把动态库拷贝到 /lib 目录下,具体操作如下图所示
可以看出,把 libb.so 拷贝到 /lib 目录之后,再编译运行,输出就正常了
注意:动态库拷贝到 /lib 或 /usr/lib 目录以后,需要再执行 sudo ldconfig 命令刷新 /etc/ld.so.cache 缓存
2、路径添加到环境变量 LD_LIBRARY_PATH
执行 export
命令把动态库路径 /home/smb/wl/mytst/sotest/lib/
加入到环境变量 LD_LIBRARY_PATH
中,然后再运行,从下图可以看到,最后输出正确
注意:使用 export
把库路径导入环境变量 LD_LIBRARY_PATH
的方法,只针对当前 SSH 连接有效,新打开一个 SSH 连接的时候,又需要重新设置,比较好的方式是把 环境变量 LD_LIBRARY_PATH
加入到 ~/.bashrc
中,这样就对当前用户有效,如果把它加到 /etc/bashrc
中,就对所有用户有效
3、路径添加到 /etc/ld.so.conf
中
如下图,打开 /etc/ld.so.config
,写入动态库路径
执行 sudo ldconfig
命令刷新 /etc/ld.so.cache
缓存,然后运行可执行程序,结果如下图
在上面打开 /etc/ld.so.config
的时候,我们发现第一行是: include ld.so.config.d/*.conf
,它表示我们也可以把动态库路径放到一个自定义的配置文件中,然后把这个配置文件拷贝到 /etc/ld.so.conf.d/
目录中即可,可执行程序加载动态库的时候,会自动去自定义配置中目录中搜索, 把库目录写入到自定义配置 my.conf
中
把自定义配置 my.conf
拷贝到 /etc/ld.so.conf.d/
目录中,并使用 ldconfig
命令刷新缓存
最后,运行可执行程序,可以看出,打印正确
使用 ldd btest_so
命令可以查看可执行程序加载了哪些动态库以及每个动态库的路径
如上图所示,红框的部分是自己编写的动态库以及动态库的路径,如果加载动态库失败,则会显示 "not found"
的字样,如下图
网站声明:如果转载,请联系本站管理员。否则一切后果自行承担。
加入交流群
请使用微信扫一扫!