本文主要介绍Linux操作系统中共享库(shared object,即so)的相关知识。
本文主要从共享库的版本控制角度来讲述。
1 概述
本文会以curl共享库libcurl为例,来展开讲述。
首先在此说明,so有三个名称,分别如下:
- realname: so的真实名称,如“libcurl.so.4.5.0”;
- soname: so的软链接名称,如“libcurl.so.4”;
- linkername: 编译过程中使用的so的名称,如curl。
2 realname
realname是so的真实名称,如编译安装curl软件后,生成的“/usr/local/lib/libcurl.so.4.5.0”。
realname在文件名中带有版本号信息,如“libcurl.so.4.5.0”的“4.5.0”。
3 soname
从版本控制的脚本来看,如果一个可执行程序直接连接到so的realname,那么当该so升级的时候,so的版本号发生了变化,那么就必须重新编译生成该可执行程序,这显然是不好的版本管理方式。为了解决此问题,出现了so的soname。soname实际上是realname的一个软链接,我们可以通过“readelf -d”命令查看so的soname,如下:
soname一般会保留so的大版本号,如“libcurl.so.4”。
在编译生成程序(可执行程序及共享库)时,我们通常都是连接的soname,这样当被依赖的so升级时,由于soname并没有变化,只需要将soname指向新的realname,即可实现被依赖的so的升级了,这样就实现了so的版本控制。
4 linkername
linkername是编译代码时的连接阶段使用的so的名字,实际上linkername也是指向realname的。
一般在程序编译时,我们会通过“-l”选项为其指定需要连接的so的名字(或是使用“CMakeLists.txt”时,为其指定的so的名字),样式类似:
-lcurl
或对应“CMakeLists.txt”中的“target_link_libraries(目标程序 ... curl ...)”,这里的curl就是so的linkername。
5 三个名称之间的关系
在编译生成程序时使用linkername进行连接的,而这个linkername实际上是指向realname的,但是在后续的使用过程中,程序其实是使用soname来找到被依赖的realname的,那么这三者是如何在编译、使用过程中连接起来的呢?
原来在编译生成realname时,已经为其指定了对应的soname(如前文所述,可通过“readelf -d”来查询),这样以soname作为桥梁,即可将so的三个名称联系起来。
结合程序的编译、生成、运行过程,描述so的三个名称关系如下:
- 程序编译时,用到了依赖库的so的linkername,即curl。由于linkername指向realname,所以程序实际上连接到了realname;
- 程序从realname处获取到运行时需要用到的soname,即libcurl.so.4;
- 编译生成程序;
- 程序运行时,调用soname,而soname实际上又指向了realname,所以程序实际上是调用的 realname。
上述几个步骤可以使用下图来表述: