共享链接库当应用程序启动时被加载。当一个共享链接库被成功安装后,所有的应用程序从此以后都会自动使用新的共享库。实际上它要比这个更灵活更复杂,因为 Linux 采用了如下方法允许你:
-
更新库但是仍然支持应用程序使用老的,不向后兼容的版本;
-
当执行应用程序时可以覆盖指定的库甚至是库中特定的函数;
-
就算应用程序正在运行并使用已经存在的库,仍然可以做以上的事情。
为了让共享链接库支持这些特性,我们必须遵循一些惯例和规范。首先你需要明白库的各种名字,特别是 soname 和 real name 。你也必须知道这些库应该被放在文件系统中的什么地方。
每一个共享链接库都有一个特别的名字 soname 。 soname 由前缀 lib ,库名,扩展名 .so ,然后是 . 和一个版本号组成。版本号会随着接口的变化而递增。最底层的 C 库是一个例外,因为它不是以 lib 打头。全限定的 soname 会以它所在的目录作为前缀。在一个运行的系统中全限定的 soname 只是 real name 的符号链接。
每个共享链接库都有一个 real name ,它是包含真正库代码文件的名字。 Real name 是由 soname 后跟一个 . ,次版本号,另一个 . ,和发布版本号组成。最后的 . 和发布版本号是可选的。次版本号和发布版本号支持配置控制,以此让你知道现在安装的库是什么版本。
另外,还有个名字用于编译器请求链接库,我们可以称之为 linker nam 。它只是 soname 去掉版本号。
管理动态链接库的关键就是区分这些名字。在应用程序中,当它们内部列出自己需要的共享链接库时,只要列出 soname 即可。恰恰相反,当你创建一个共享链接库时,你只需要创建一个给定名字的库即可 ( 可以附带更具体的版本信息 ) 。当安装库的一个新版本时,你把它安装在某个特定的文件夹然后运行程序 ldconfig 。 Ldconfig 会检测已经存在的文件并创建 soname 符号连接到 real name ,同时也会建立缓存文件 /etc/ld.so.cache 。
Ldconfig 并不会建立 link name 。典型地这应该在库安装的时候完成, linker name 只是简单地作为最新的 soname 或者 real name 的符号链接。我本推荐 linker name 应该是 soname 的符号链接,因为在大多数场合如果你更新库,你总是希望在链接时自动使用它。我问过 H.J.Lu 为什么 ldconfig 不自动建立 linker name 。他的解释基本上是你可能想要使用最新的版本运行代码,但是要用老版本来开发。所以, ldconfig 不会假设你到底需要把应用程序链接到什么版本,所以,安装程序要明确修改符号链接来更新链接器用什么版本的库。
因此, /usr/lib/libreadline.so.3 是一个全限定的 soname , ldconfig 会设置符号链接到某个 real name 比如 /usr/lib/libreadline.so.3.0 。也会有一个 linker name , /usr/lib/libreadline.so ,一个指向 /usr/lib/libreadline.so.3 的符号链接。
共享链接库必须被放在文件系统的某个地方。大多数开源软件都倾向于遵循 GNU 标准。 GNU 标准推荐当发布源代码时把所有库默认安装在 /usr/local/lib 下,所有的命令安装在 /usr/local/bin 下。它也定义了一些关于如何覆盖这些默认设置和调用安装例程的约定。
但是 Filesystem Hierarchy Standard(FHS) 也讨论了应该在发布时将库安装在什么目录下。根据 FHS 规范,大部分库都应该安装在目录 /usr/lib ,但是启动时所需要的库必须在目录 /lib ,非系统库应该在目录 /usr/local/lib 。
这两份文档的规定并不冲突: GNU 规范推荐的默认目录是针对源代码的开发人员,而 FHS 推荐的默认目录是对发布者而言的。注意,如果你的库调用了一些程序,并且这些程序只能被库调用,那么你应该把这些程序放在 /usr/local/libexec 目录下 ( 对于发布时的 /usr/libexec 目录 ) 。
在 GNU 基于 glibc 的系统中,包含所有的 Linux 操作系统,启动 ELF 二进制可执行文件都会导致程序加载器被自动加载并执行。在 Linux 下,加载器是 /lib/ld-linux.so.X(X 是版本好 ) 。它会依次找到并加载所有其他应用程序用到的共享链接库。
所以的搜索目录在文件 /ect/ld.so.conf 中指定。很多 Red Hat 衍生的 Linux 发行版在该文件中并不包含目录 /usr/local/lib 。
如果你只是想覆盖库中的一些方法,但是保持库的其他部分不变,你可以在文件 /etc/ld.so.preload 中输入该库的名字。这个方法典型用于紧急的 patch 。发行版在发布时通常不会包含这样的文件。
在程序启动时搜索所有的目录会很低效,所以缓存会被用到。 ldconfig 默认会读取文件 /etc/ld.so.conf ,建立合适的符号链接到动态链接目录,接着会写缓存到文件 /etc/ld.so.cache ,然后这些动态链接库就可以被其他程序调用了。这大大加速了访问链接库的时间。