当我这样做ls -l
时,/usr/lib
我看到很多带有"sameName.so.*.*"
扩展名的库。
- 这些扩展的意义是什么?
- 为什么要创建软链接?它们有什么用?
一个例子将有助于理解。
这是用于版本共享目标文件的技巧。这是一种避免由于延迟链接而出现的可怕的 DLL 地狱的方法。
延迟链接(或后期绑定)的优点是可以更改可执行文件的组件,而无需实际重新链接这些可执行文件。这允许在第三方组件中修复错误,而无需发布新的可执行文件等。
缺点与优点完全相同。您的可执行文件可以发现它对底层库所做的假设已更改,这可能会导致各种问题。
共享对象的版本控制是避免这种情况的一种方法。另一个是根本不共享对象,但这也有优点和缺点,我不会在这里讨论。
例如,假设您有xyz.so
. 您有一个文件和指向该文件的符号链接:
pax> ls -al xyz*
-rw-r--r-- 1 pax paxgroup 12345 Nov 18 2009 xyz.so.1
lrwxrwxrwx 1 pax paxgroup 0 Nov 18 2009 xyz.so -> xyz.so.1
现在,当您创建一个可执行文件时exe1
,将其与 链接xyz.so
,它将遵循符号链接,以便它xyz.so.1
作为运行时需要加载的内容存储在可执行文件中。
这样,当您升级共享库时:
pax> ls -al xyz*
-rw-r--r-- 1 pax paxgroup 12345 Nov 18 2009 xyz.so.1
-rw-r--r-- 1 pax paxgroup 67890 Nov 18 2009 xyz.so.2
lrwxrwxrwx 1 pax paxgroup 0 Nov 18 2009 xyz.so -> xyz.so.2
您的原始可执行文件exe1
仍将加载共享对象的版本 1。
但是,您现在创建的任何可执行文件(例如exe2
)都将与共享对象的版本 2 链接。
实际的实现细节可能会有所不同(我的答案基于早期的 UNIX,而 Linux 似乎比仅仅遵循符号链接更智能地进行版本控制)。IBM developerWorks 有一篇很好的文章,介绍了它是如何完成的。
当你创建一个共享对象时,你给它一个真实的名字和一个soname
. 这些用于安装共享对象(创建对象和指向它的链接)。
因此,您最终可能会遇到以下情况:
pax> ls -al xyz*
-rw-r--r-- 1 pax paxgroup 12345 Nov 18 2009 xyz.so.1.5
lrwxrwxrwx 1 pax paxgroup 0 Nov 18 2009 xyz.so.1 -> xyz.so.1.5
lrwxrwxrwx 1 pax paxgroup 0 Nov 18 2009 xyz.so -> xyz.so.1
拥有xyz.so.1.5
的。SONAME
_xyz.so.1
当链接器链接到 时xyz.so
,它会一直跟随链接到xyz.so.1.5
并使用其存储在可执行文件中SONAME
。xyz.so.1
然后,当您运行可执行文件时,它会尝试加载xyz.so.1
指向特定xyz.so.1.N
(不一定是 1.5 版)的文件。
因此,您可以安装xyz.so.1.6
并更新xyz.so.1
链接以指向它,而已经链接的可执行文件将使用它。
这种多层方法的一个优点是您可以拥有多个可能不兼容的同名库(xyz.so.1.*
, xyz.so.2.*
),但是在每个主要版本中,您可以自由升级它们,因为它们应该是兼容的。
当您链接新的可执行文件时:
xyz.so
人将获得最新主要版本的最新次要版本。xyz.so.1
将获得特定主要版本的最新次要版本。xyz.so.1.2
将获得特定主要版本的特定次要版本。这是共享库的版本控制方案。每个库都应该有 3 个名称:
libfoo.so.1.2.3
libfoo.so.1.2
. 这个名字实际上是写在库二进制文件中的,并且会在链接时记录在可执行文件中。它通常是库真实名称(通常是最新版本)的符号链接。假设您安装了libfoo
版本 1:libfoo.so
-> libfoo.so.1.0
-> libfoo.so.1.0.0
。您bar
使用-lfoo
. 由于 SONAME,它现在链接到libfoo
并在运行时加载。然后,您通过替换真正的二进制文件libfoo.so.1.0
升级到已修补但二进制兼容的版本。仍然链接到并且不需要重建。libfoo.so.1.0.1
bar
libfoo.so.1.0
现在假设您要构建一个新程序baz
,以利用 libfoo v1.1 中不兼容的更改。您安装了新版本,您的系统现在并行安装了两个版本:
libfoo.so.1.0
->libfoo.so.1.0.1
libfoo.so
-> libfoo.so.1.1
->libfoo.so.1.1.0
注意链接器名称已更新到最新版本(这是与您安装的头文件对应的版本/usr/include
)。
您构建baz
,它链接到libfoo.so
并在运行时加载libfoo.so.1.1
。并不是说bar
仍然存在冲突libfoo.so.1.0
并且不需要更新。