一 创建并编译共享库
动态链接库一般以lib开头,形如libmymodule.so.1.0.0.

后面跟的三个版本号,从左到右的含义为:

(1) 大版本号,当接口变得和之前不兼容,则新增一个大版本号。

(2) 一般增加了接口,不过旧的接口不变,则新增此版本号。

(3) 接口不做任何变化,只是实现做了修改,则新增此版本号。

假设我们的库只包含 module.cpp, 则用命令

[c-sharp] view plaincopy
g++ -fPIC -Wall -c module.cpp

生成module.o,再用命令

[c-sharp] view plaincopy
g++ -shared -Wl,-soname,libmymodule.so.1 -o libmymodule.so.1.0.0 module.o

生成libmymodule.so.1.0.0共享库。这里的libmymodule.so.1.0.0也叫做共享库的real name.

-fPIC是使生成的目标文件“位置无关(Position Independent Code)”从而可以被多个程序共享。
-shared 指定产生共享库。
-Wl,-soname,libmymodule.so.1 指定共享库的soname为libmymodule.so.1,若不指定,则无soname. soname的作用后面会提到。可以用objdump -p libmymodule.so.1.0.0 | grep SONAME 查看soname.
一般soname带且只带大版本号,比如这里的libmymodule.so.1, 是因为共享库的相互兼容的不同版本,都具有相同的soname(在升级的时候,需要用soname来对应)。而如果大版本号变了,意味着接口变得不兼容了,也就没有必要让这个版本和之前的共享库兼容了。这个时候,启用一个新的soname是更好的做法。

二 编译主程序
因为gcc中,用-l参数指定的库文件必须满足格式lib*.so 所以我们需要建立软连接libmymodule.so, 并令其指向libmymodule.so.1.0.0

[c-sharp] view plaincopy
ln -s libmymodule.so.1.0.0 libmymodule.so
这里的libmymodule.so就是所谓的"link name"。

假设我们的主程序(相对于库来说),代码在main.cpp里,则用命令

[c-sharp] view plaincopy
g++ -o main main.cpp -L. -lmymodule
生产可执行程序main。注意link name只在编译的时候需要,主程序会记住根据这个文件最终所指的共享库的soname,用来在运行的时候,查找加载动态库。如果共享库没有指定soname, 那么主程序会记住这个'link name",也就是lib*.so这样形式的名字,做为soname, 在运行的时候,用这个名字来查找加载动态库。

-L指定搜索库的文件夹,
-l 指定所依赖的库。注意这里需要去掉前面的“lib”和后面的“.so”
三 运行期
这个时候,运行./main会出现以下错误:

./main: error while loading shared libraries: libmymodule.so.1: cannot open shared object file: No such file or directory

使用ldd main可以查看所依赖的动态库是否被满足,执行“ldd main":

发现有一行libmymodule.so.1 => not found,说明在运行期,加载器没有找到对应的共享库文件。

加载器会在以下地方查找main程序中记住的所需要的库的soname:

(1) /etc/ld.so.cache 这是一个cache,存放soname到共享库文件的soname link (一个文件名为soname的软连接) 的映射(key->value值对)。

(2) /usr/lib 和 /lib

(3) 环境变量LD_LIBRARY_PATH指定的文件夹。

可以用ldconfig 命令,解决这个问题。ldconfig主要做2件事情:

一是扫描/lib和/usr/lib和/etc/ld.so.conf里指定文件夹,对里面的共享库建立soname link (ldconfig会根据文件名里的版本号,自动找到最新的共享库文件,并把soname link指向这个最新的共享库文件)

二是更新/etc/ld.so.cache,建立soname到soname link的映射。

加载器如果在ld.so.cache中查到所需的soname,则会依次找到 soname-->soname link-->实际的共享库文件。如果找不到,则会在/usr/lib和/lib中查找文件名为soname的文件,作为共享库文件加载。注意,在这里,加载器不会去找/etc/ld.so.conf里指定的文件夹,只会找/usr/lib和/lib。所以如果在/etc/ld.so.conf里指定的文件夹,增加了soname link,必须要运行ldconfig来更新ld.so.cache。

ldconfig -n /path/to/dir 命令可以指定某文件夹,不过只会做第一件事,也就是建立soname link-->实际的共享库文件。不会更新ld.so.cache 所以不需要root权限。

ldconfig -p 命令可以打印ld.so.conf里已经有的键值对。

好了,了解了以上知识,我们可以用以下方法解决找不到共享库的问题:

(a) 将libmymodule.so.1.0.0拷到/lib或者/usr/lib里,或者/etc/ld.so.conf指定的文件夹,然后执行sudo ldconfig. 于是ldconfig会自动在/lib或/usr/lib或/etc/ld.so.conf指定的文件夹里生成soname link (文件名为libmymodule.so.1) 指向libmymodule.so.1.0.0,然后在ld.so.cache中增加libmymodule.so.1到/lib /libmymodule.so.1或/usr/lib/libmymodule.so.1的映射(前者是个名字,后者是个软连接文件)

(b) 使用LD_LIBRARY_PATH=/PATH/TO/SO ./main来运行程序。前提是在/PATH/TO/SO中建立一个名字为soname的软连接,使其指向实际的共享文件。

(c) 在/lib或/usr/lib中,手动建立软连接 libmymodule.so.1 令其指向实际的共享库文件 libmymodule.so.1.0.0。 因为前面提到的,加载器在ld.so.cache中找不到要找的键为soname的项,会在/lib或/usr/lib中找名字为soname的文件。这方法虽然可以用,不过感觉比较粗暴。不推荐。

四 升级共享库
如果是用上述(a)方法使用共享库的,只需要将新的共享库,比如 libmymodule.so.1.0.1拷到原来的目录(/lib或/usr/lib或者/etc/ld.so.conf指定的文件夹),然后运行 ldconfig即可。ldconfig会更新soname link, 使其指向最新的共享库文件libmymodule.so.1.0.1,ld.so.cache不需要更新。

如果是用上述(b)(c)方法是用共享库的,需要更新相应的软连接,使其指向最新的共享库文件。

五 动态加载共享库
在主程序中动态加载共享库时,如果指定的是绝对路径("/"开头的),则加载该绝对路径指向的共享库文件。如果不是绝对路径,而是一个文件名,则将这个文件名当作soname,然后依照上述的方法,查找加载相应的共享库文件。

六 注意点
如果在共享库中,要用到主程序的变量或者方法,要在编译主程序时,加上-rdynamic参数,使得所有名字在共享库的空间中可见。

linux中安装,编译时调用,运行时调用,更新共享库相关推荐

  1. linux aspnet服务器,在Linux中安装ASPNET.Core3.0运行时的示例代码

    摘要: # 以下示例适用于x64位runtime v3.0.0mkdir /runtimescd /runtimeswget https://... # 以下示例适用于x64位runtime v3.0 ...

  2. Linux中如何获得进程的运行时堆栈

    关于这个话题,我们一般是为了处理一下生产环境中程序出现死循环或者死锁等问题.我们一般想到的方法就是gdb attach上一个运行中的进程.但是这个需要手动交互.通过网上查找和实践,可以有以下几种选择: ...

  3. linux中安装openoffice,及解决转pdf时中文乱码或者中文不显示问题【离线】

    linux中安装openoffice[离线] 目录安装openoffice启动openoffice查看openoffice运行状态启动时可能出现的问题openoffice在转pdf时,中文乱码或者中文 ...

  4. java中编译类型的方法 和 运行时的类型方法 有什么区别

    1:引言 这是在复习多态当中,看到不太理解的东西, 就是 Java编译类型和运行类型 2:多态 多态首先得是在 有继承关系和方法重写的类当中:指同一个方法在被调用时,由于对象不同则会有不同的效果(). ...

  5. Qt编译通过,运行时出现the process was ended forcefully问题的解决方案

    ** Qt编译通过,运行时出现the process was ended forcefully问题的解决方案 ** Debug和Release模式下编译均能通过,调用外部函数也不会提示错误,但是运行就 ...

  6. 【Java 19】反射 - 反射机制概述、获取Class实例、类的加载与ClassLoader的理解、创建运行时类的对象、获取运行时类的完整结构、调用运行时类的指定结构、动态代理

    反射机制概述.获取Class实例.类的加载与ClassLoader的理解.创建运行时类的对象.获取运行时类的完整结构.调用运行时类的指定结构.动态代理 反射 1 Java反射机制概述 1.1 Java ...

  7. ActiveReports中如何在后台导出运行时绑定数据源报表

    ActiveReports支持运行时绑定数据源功能,这种绑定数据源方法使用较为普及,然而很多系统中都需要在后台导出报表文件,所以用户就很困惑,ActiveReports中如何在后台导出运行时绑定数据源 ...

  8. nameof() 到底是编译时还是运行时行为?

    咨询区 Gigi: 在 C#6.0 中,可以用 nameof() 直接获取变量或者类型的名字,请问这是一个 编译时 还是 运行时 行为? 回答区 Faris Zacina: 可以肯定的说,它是一种 编 ...

  9. Linux中使用userdel命令删除用户时出现错误 “userdel: user XXX is currently used by process XXX”*

    Linux中使用userdel命令删除用户时出现错误 "userdel: user XXX is currently used by process XXX" [root@loca ...

最新文章

  1. 【c语言】蓝桥杯算法提高 温度转换
  2. 今年阿里双十一CDN要冲历史之最,峰值带宽达到5000G+,来高手分析一下他们的CDN节点数量和规模...
  3. RNN梯度消失和爆炸的原因 以及 LSTM如何解决梯度消失问题
  4. 转贴 jQuery Datepicker by Example
  5. 20211江西高考成绩查询,江西高考成绩查询系统
  6. 初学Hibernate
  7. 【剑指offer】面试题49:丑数
  8. (转)iOS编程高性能之路-自动化编译脚本(1)
  9. java中常见的编译错误的是_编译时JAVA最常见的错误有哪些
  10. anconda设置镜像源_三、DockerFile 定制属于自己的专属镜像
  11. MySQL日志及主从复制实现
  12. 拼装机器人感想_机器人学习心得总结
  13. java总结体会_Java课程总结心得体会
  14. [深度学习从入门到女装]PReLU
  15. 中国手机用户换机越来越慢
  16. 区块链技术指南学习(五)双花
  17. 三、Python复习教程(重点)- 前端框架实战
  18. 利用Vant完成登录页面!
  19. 实现css文字垂直居中的8种方法
  20. npm ERR! Error: EACCES: permission denied, access '/usr/local/lib/node_modules'

热门文章

  1. 解决:mysql5.7 timestamp默认值0000-00-00 00:00:00 报错
  2. 如何滚动更新 Service?- 每天5分钟玩转 Docker 容器技术(102)
  3. s:url多值传递的时候出现;amp
  4. 移动端H5 页面 input 获取焦点不灵敏
  5. 超长干货 | Kubernetes命名空间详解
  6. UrlRewriter URL重写
  7. 华为P10的内存门和闪存门的检测方法
  8. 移动开发之设计稿转换页面单位尺寸
  9. 微信公众平台开发者原理图解
  10. jquery-easyui中表格的行编辑功能