读书-程序员的自我修养-链接、封装与库(22:第八章:Linux共享库的组织)
1. 共享库管理
1.1 为什么需要共享库管理?
因为动态链接的优点,导致程序中有大量的动态库或者叫做共享对象的存在。
如果没有进行管理,那么多的共享对象就可能存在各个目录下,给长期维护和升级带来不便。
故需要对共享对象进行管理包括目录存放,命名规则等。
1.2 共享库和共享对象关系
共享库(Shared Library),Linux共享库其实就是共享对象。
由于共享对象可以再程序之间共享,就导致其以库的形式存在了,所以开发者都是库以共享对象的形式存在。
故: 共享库和共享对象可以当做同一个东西。
1.3 共享库的兼容性
共享库的跟新分为两类:
- 兼容跟新:新增新内容,原接口不变
- 不兼容跟新:原接口改变
1.4 共享库命名方式
linux对共享库的命名规则如下: libNAME.x.y.z.so
- 以lib开头
- .so 结尾
- name 表示库的名字
- x 表示主版本号
主版本号表示库的重大升级,因此不同主版本号之间库不兼容 - y 表示次版本号
次版本号表示增量升级,因为不同次版本号之间库可兼容 - z 表示发布版本号
发布版本号表示错误修正,性能优化等
1.5 SO-NAME
1.5.1 背景
从程序角度看,程序运行时怎么知道它需要哪些库?版本号又是什么?
SO-NAME 来解决这个问题。
1.5.2 定义
Linux 采用一种叫做 SO-NAME 的命名机制来记录各个共享库的依赖关系。
每个共享库,都有一个 SO-NAME。
它去除次版本号和发布版本号,如 libNAME.x.y.z.so 的SO-NAME 是 libNAME.x
1.5.3 作用
SO-NAME 通过软链接实现,它会指向主版本号相同,次版本号和发布版本号最新的共享库。
1.5.4 SO-NAME原则
SO-NAME表示一个库的接口,接口不向后兼容,SO-NAME就发生变化。这是基本原则。
1.5.5 ldconfig
linux中有个工具 ldconfig 它会遍历共享库,然后指向最新的共享库。
1.6 链接名
libNAME.x.y.z.so 的 NAME 就叫做链接名。
如链接的时候只要, -lNAME ,如 -lm 就是 libm.so 数学库
2. 符号版本
2.1 基于符号的版本机制
思想是:让每个导入和导出符号都有一个相关联的版本号。类似于符号修饰
例子:
如 libHELLO.1.1.1.so 的升级版本 libHELLO.1.2 libHELLO.1.3都保留 libHELLO.1.1 的SO-NAME,
但是给新版本(1.2 1.3等)中新增的那些符号打上标记 如 VER_1.3
3. 共享库系统路径
3.1 FHS
Filesystem Hierarchy Standard(文件系统层次化标准)
FHS 规定了系统中文件目录该如何存放,已经各个目录的结构,组织和作用等。
3.2 系统中存放共享库的位置
FHS 规定系统中两个存放共享库的位置。
-
/lib 存放系统最关键和基础的共享库
如 动态连接器 c语言运行库 数学库等 -
/usr/lib 非系统运行时需要的关键共享库。
主要是开发过程中用得到的共享库。 -
/usr/local/lib 存放非操作所需的第三方软件用到的库
如python相关的共享库
4. 共享库查找过程
4.1 ldconfig 作用
- 作用1是为各个共享库目录各个共享库创建,删除,跟新相应的SO-NAME(即相应的符号链接)。
- 作用2建立一个所有共享库的 SO-NAME 的缓存
5. 环境变量
5.1 LD_LIBRARY_PATH
- 环境变量 LD_LIBRARY_PATH 可以临时的改变共享库的查找路径,而不会影响系统运行。
- LD_LIBRARY_PATH 是由若干路径组成的环境变量,它们以冒号隔开。
- LD_LIBRARY_PATH 默认是空的
- 当我们设置了 LD_LIBRARY_PATH,那么进程在启动时,动态链接器在查找共享库时,首先去 LD_LIBRARY_PATH 中查找指定的目录
- 优点: 让我们方便的测试新的共享库和非标准的共享库
5.2 动态链接器查找动态库的顺序
有了 LD_LIBRARY_PATH 之后,动态链接器查找动态库的顺序为:
- 由环境变量 LD_LIBRARY_PATH 指定的路径
- 由路径缓存文件 /etc/ld.so.cache 指定的路径
- 默认共享路径 先 /usr/lib 再 /lib
6. 共享库的创建和安装
6.1 共享库的创建
-shared 表示结果是共享库类型的
-fPIC 使用地址无关代码技术
如: 创建动态链接库:
gcc -shared -fPIC -o libmyhello.so hello.o
6.2 共享库构造和析构函数
6.2.1 背景
很多时候,我们希望共享库在被装载时能够进行一些初始化工作,如文件打开,网络连接,使得共享库里面的接口能够正常运行。
6.2.2 attribute(constructor) 和 attribute(destructor)
GCC提供了共享库的构造函数,只要在函数声明前加上__attribute__(constructor)属性,就指定该函数为共享库的构造函数。
有这个属性的函数会在共享库加载时被执行,即在main之前被执行。
对于的析构函数就是 attribute(destructor)