Linux中的共享库之版本管理

Posted createchance

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux中的共享库之版本管理相关的知识,希望对你有一定的参考价值。

共享库也就是动态库,在linux中是随处可见的,这是由于动态链接有这众多优点,因此大量的程序开始使用动态链接的机制,所以你才会在linux这样的操作系统中看到大量的动态链接库的存在。但是随着linux系统架构的成熟,应用和系统软件生态系统的繁荣,导致linux中的动态库数量越来越多,并且同一个共享库还会有不同的版本,这个时候如果没有一个良好的动态库管理机制,那么势必会给长期的维护,升级造成极大的困难。
本文我们先看一下linux系统是怎么维护不同版本的库的。

Linux中共享库的命名规则

想要管理好系统中的众多共享库这就需要有一个良好的命名规则,不能出现各个共享库随意命名的情况。一般而言,在linux中的共享库的命名规则如下:

libname.so.x.y.z

最前面的前缀lib是所有共享库都会使用的前缀,表示这是一个库文件;之后的name就是这个共享库的名字;然后的so表示shared object,是一个共享目标文件,这样是共享库的实质;然后的x.y.z表示版本,其中x表示主版本号,y是次版本号,z是发布版本号,这三者意义是不同的。
主版本表示共享库的重大升级,很多时候不同主版本号之间的库是互相不兼容的,因为某些接口可能已经被修改,而旧的接口没有保留。这样的话,如果那些依赖于旧版本的程序可能需要重新编译,或者系统保留旧版本的库才行。
次版本号表示库增量式升级,通常也就是增加一些新的接口,而原来的库符号则保留不变,保证兼容,因此如果是次版本号的升级不会导致系统中的某些程序无法运行的情况。
发布版本号表示库的一些bug修正,性能的提升改进的升级,通常不会添加任何接口,也不会对任何接口进行修改。
因此通常来讲,相同主版本号,相同次版本号,不同发布版本号之间的库是完全兼容的;相同主版本号,不同次版本号之间也是兼容的。
这里需要说明一下,在linux中很多库是延续unix中而来的,由于这个历史原因linux中的一些库是不遵循上述的命名规则的。最著名的例子就是要数基本C库了,这个库的命名规则如下:

libc-x.y.z.so

这种命名方式和我们上面讲的不一样,这一点大家需要注意。如果你的手上有linux系统的话,你可以查看一下你的系统中的libc库是不是这样的命名的,以我的debian 8.5 amd64为例,libc库在/lib/x86_64-linux-gnu目录下:

链接器是怎么知道程序使用哪个版本的库的?

这是一个很好的问题,我们前面说道了,共享库的主版本和次版本基本上就确定了一个共享库的接口版本,这个版本我们通常称之为ABI版本。现在问题来了,一个程序在编译的时候不可能先查看系统中有哪些版本的库,一般都是直接在makefile或者gcc命令选项中说明需要依赖哪个库,并没有给出具体库的版本,既然这样,linux操作系统在加载这个程序到内存运行的时候是怎么知道这个程序具体依赖哪个库的呢?
为了弄明白这个问题,我们这里首先自己编写一个demo.c程序,然后把它编译成可执行文件。这个可执行文件一定是依赖libc库的,我们可以使用readelf命令查看这个elf文件的头部,实际看一下它究竟依赖什么库。
demo.c

#include <stdio.h>

int main()

    return 0;

程序很简单,然后我们使用如下命令编译:、

gcc demo.c -o demo

生成了一个名称为demo的可执行程序,现在我们使用readelf命令读取demo的头部信息:

关于readelf命令的使用请执行man查看,这里我们使用-d参数去读取demo的动态符号表的信息,这个表中就保存了demo中所有重要的符号的依赖信息。我们看到这个表中的第一个行就是一个NEED依赖库,很明显它依赖的库名是libc.so.6。到这里大家可能有点疑惑,我们前面讲的libc的命名规则由于历史原因不是这样的啊,这里使用这个名字能找到吗?带着这个疑问我们再次回到/lib/x86_64-linux-gnu目录下,也就是libc的老家,我们看下这个目录下所有以libc开头的文件:

非常好!我们看到了一个叫做libc.so.6的软链接了,它链接到我们libc-2.19.so这个实际的c库了!那么这个软链接是何人在什么时候生成的呢?这就涉及到linux的SO-NAME机制了。

SO-NAME

在linux中,普遍采用一种成为SO-NAME的方式来记录程序和共享库之间的依赖关系。每个共享库都会有一个SO-NAME,这个SO-NAME即共享库的文件名去掉次版本号和发布版本号,保留主版本号之后的名字。比如一个共享库的名字是:

libdemo.so.1.2.3

那么它的SO-NAME就是libname.so.1,很明显SO-NAME规定了共享库的接口,SO-NAME相同的两个库互相之间一定是兼容的。在linux系统中,系统会为每个共享库创建一个跟SO-NAME相同的并且指向该共享库的一个软链接,这就造成我们上面看到的那个软链接了。
SO-NAME软链接有什么用呢?实际上这个软链接会指向系统中主版本号相同,次版本和发布版本不同的各个库中版本最新的那个库。也就是说如果你的系统中有以下两个库:

libdemo.so.1.1.1
libdemo.so.1.2.3

那么libdemo.so.1软链接会指向libdemo.so.1.2.3这个库。因此有了SO-NAME软链接之后就会使得程序在依赖某个共享库的时候,在编译,链接,运行的时候都使用SO-NAME软链接指向的库,而不是具体的某个版本的库,这样就使得我们的程序在主版本号相同,次版本号和发布版本号不停变化的库之间都能兼容运行,不会出错,而我们也不用做任何工作,因为SO-NAME软链接会指向最新的那个库。同时,如果系统中存在主版本号不同的库的话,SO-NAME软链接也会不同,如上的例子如果你的系统升级了libdemo库到libdemo.so.2.0.0的话,那么系统中会多一个名为libdemo.so.2的软链接,原来的libdemo.so.1软链接依然指向1.x版本的最新库,这样系统中那些依赖于旧版本库的程序就依然能够正常链接运行。
在linux系统中,提供有一个叫做ldconfig的程序,当系统中安装或者更新一个库的时候,就需要运行这个工具,他会遍历所有默认共享库的位置,比如/lib,/usr/lib等目录,然后更新他们的软链接,使他们指向最新的共享库版本;如果是安装共享库的话,ldconfig会创建一个软链接。

以上是关于Linux中的共享库之版本管理的主要内容,如果未能解决你的问题,请参考以下文章

Python常用标准库之os

Python常用标准库之os

GitGit的简介安装与本地仓库文件可视化管理

最火Kotlin库之Anko详解

Linux用户管理

Python 标准库之 fcntl