什么是 *.so.*.* 库
当我在 /usr/lib
中执行 ls -l
时,我看到很多带有 "sameName.so.*.*"
扩展名的库。
- 这些扩展有何意义?
- 为什么要创建软链接?它们有什么用?
一个例子会对理解有很大帮助。
When I do ls -l
in /usr/lib
I see lots of libs with "sameName.so.*.*"
extension.
- What is the significance of these extensions?
- Why softlinks are created? what are their use?
One example will help a lot in understanding.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这是一个共享库的版本控制方案。每个库应该有3个名称:
libfoo.so.1.2.3
libfoo.so.1.2
。该名称实际上写在库二进制文件本身内部,并将在链接时记录在可执行文件中。它通常是库真实名称(通常是最新版本)的符号链接。示例
假设您安装了
libfoo
版本 1:libfoo.so
->libfoo.so.1.0
->libfoo.so.1.0.0
。您可以使用-lfoo
构建程序bar
。它现在链接到libfoo
并将由于 SONAME 在运行时加载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
运行并且不需要更新。It's a versioning scheme for shared libraries. Every library should have 3 names:
libfoo.so.1.2.3
libfoo.so.1.2
. This name is actually written inside the library binary itself, and will be recorded in the executable at link time. It is usually a symlink to library's real name (usually latest version).Example
Say you have
libfoo
version 1 installed:libfoo.so
->libfoo.so.1.0
->libfoo.so.1.0.0
. You build your programbar
with-lfoo
. it now links tolibfoo
and will loadlibfoo.so.1.0
at runtime due to SONAME. Then you upgrade to a patched but binary-compatiblelibfoo.so.1.0.1
by replacing real binary.bar
still links tolibfoo.so.1.0
and doesn't need to be rebuilt.Now imagine you want to build a new program
baz
that takes advantage of incompatible changes in libfoo v1.1. You install new version and your system now have two versions installed in parallel:libfoo.so.1.0
->libfoo.so.1.0.1
libfoo.so
->libfoo.so.1.1
->libfoo.so.1.1.0
Note linker name was updated to the latest version (this is the version corresponding to the headers you installed in
/usr/include
).You build
baz
, and it links tolibfoo.so
and loadslibfoo.so.1.1
at runtime. Not thatbar
still runs againstlibfoo.so.1.0
and doesn't need to be updated.这是用于对共享对象文件进行版本控制的技巧。这是避免由于惰性链接而产生的可怕的 DLL 地狱的一种方法。
延迟链接(或后期绑定)的优点是可以更改可执行文件的组件,而无需实际重新链接这些可执行文件。这允许在第三方组件中修复错误,而无需发布新的可执行文件等。
缺点与优点完全相同。您的可执行文件会发现它对底层库所做的假设已更改,这可能会导致各种问题。
共享对象的版本控制是避免这种情况的一种方法。另一种方法是根本不共享对象,但这也有优点和缺点,我不会在这里讨论。
举例来说,假设您拥有
xyz.so
版本 1。您有一个文件和到该文件的符号链接:现在,当您创建一个可执行文件
exe1
并将其与xyz.so
链接时,它将遵循符号链接,因此它将 xyz.so.1 存储在可执行文件中,作为运行时需要加载的内容。这样,当您升级共享库时:
您的原始可执行文件
exe1
将仍然加载共享对象的版本 1。但是,您现在创建的任何可执行文件(例如
exe2
)都将与共享对象的版本 2 链接。实际的实现细节可能会有所不同(我的答案基于早期的 UNIX,而 Linux 似乎比仅仅遵循符号链接更智能地进行版本控制)。 IBMdeveloperWorks 在这里有一篇很好的文章介绍了它是如何完成的。
创建共享对象时,您需要为其指定真实名称和
soname
。它们用于安装共享对象(这会创建对象及其链接)。因此,最终可能会出现这样的情况:
xyz.so.1.5
拥有xyz.so.1
的SONAME
。当链接器链接到
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
链接的人将获得特定主要版本的特定次要版本。This is a trick used to version shared object files. It's a way of avoiding the dreaded DLL hell which came about because of lazy linking.
The advantage of lazy linking (or late binding) is that components of your executable can be changed without actually re linking those executables. This allows for bug fixes in third party components without having to ship a new executable, among other things.
The disadvantage is exactly the same as the advantage. Your executable can find that assumptions it made about the underlying libraries have been changed and this is likely to cause all sorts of issues.
Versioning of shared objects is one way to avoid this. Another would be to not share objects at all but that also has pros and cons which I won't get into here.
By way of example, let's say you have version 1 of
xyz.so
. You have a file and a symbolic link to that file:Now, when you create an executable file
exe1
, linking it withxyz.so
, it will follow the symbolic link so that it storesxyz.so.1
in the executable as the thing it needs to load at runtime.That way, when you upgrade the shared library thus:
your original executable
exe1
will still load version 1 of the shared object.However, any executables you create now (such as
exe2
) will be linked with version 2 of the shared object.The actual implementation details may vary somewhat (I'm basing my answer on earlier UNIXes and Linux appears to do versioning a little more intelligently than just following symbolic links). IBM developerWorks has a nice article on how it's done here.
When you create a shared object, you give it both a real name and an
soname
. These are used to install the shared object (which creates both the object and a link to it).So you can end up with the situation:
with
xyz.so.1.5
possessing theSONAME
ofxyz.so.1
.When the linker links in
xyz.so
, it follows the links all the way toxyz.so.1.5
and uses itsSONAME
ofxyz.so.1
to store in the executable. Then, when you run the executable, it tries to loadxyz.so.1
which will point to a specificxyz.so.1.N
(not necessarily version 1.5).So you could install
xyz.so.1.6
and update thexyz.so.1
link to point to it instead and already-linked executables would use that instead.One advantage of this multi-layer method is that you can have multiple potentially incompatible libraries of the same name (
xyz.so.1.*
,xyz.so.2.*
) but, within each major version, you can freely upgrade them since they're supposed to be compatible.When you link new executables:
xyz.so
will get the latest minor version of the latest major version.xyz.so.1
will get the latest minor version of a specific major version.xyz.so.1.2
will get a specific minor version of a specific major version.