库:静态还是共享?

库:静态还是共享?

最初的库仅仅是例程的归档文件,从中提取所需的例程并链接到可执行程序中。这些被称为静态库,在类 UNIX 操作系统上名称形式为 libfoo.a。在某些旧操作系统上,它们是唯一可用的类型。

在几乎所有 Linux 平台上,还有“共享”(或等价地称为“动态”)库(名称形式为 libfoo.so)——库的一份副本被加载到虚拟内存中,并由所有调用其任何函数的程序共享。这在空间上更高效。

在过去,诸如 shell 之类的关键程序通常被静态链接,以便即使共享库(如 libc.so)损坏(例如在非正常关机后执行 fsck 时被移至 lost+found),也能存在某种形式的最小恢复系统。如今,大多数人在需要恢复时会使用备用系统安装盘或 U 盘。日志文件系统也降低了此类问题发生的可能性。

在本书中,多处使用了诸如 --disable-static 之类的配置开关,也讨论了使用系统版本库而非其他软件包内部所包含版本的可能性。其主要原因是为了简化库的更新。

如果某个软件包链接到动态库,那么一旦安装了较新的库版本并(重新)启动该程序,更新到较新库版本就是自动的(前提是库的主版本号不变,例如从 libfoo.so.2.0 升级到 libfoo.so.2.1。升级到 libfoo.so.3 则需要重新编译——可使用 ldd 找出哪些程序使用了旧版本)。如果程序链接到静态库,则始终必须重新编译该程序。如果您知道哪些程序链接到特定的静态库,这仅仅是个麻烦事。但通常您并 知道需要重新编译哪些程序。

识别何时使用静态库的一种方法,是在每个软件包安装结束时处理它。编写一个脚本,在 /usr/lib 或您安装到的任何位置查找所有静态库,然后将它们移到另一个目录,使链接器不再能找到它们,或者将它们重命名,例如将 libfoo.a 改为 libfoo.a.hidden。这样,如果将来需要,可以临时恢复静态库,并识别出需要它的软件包。但不应盲目执行此操作,因为许多库仅以静态版本存在。例如,glibcgcc 软件包中的某些库应始终存在于系统上(截至 glibc-2.36 和 gcc-12.2,这些库包括 libc_nonshared.a、libg.a、libpthread_nonshared.a、libssp_nonshared.a、libsupc++.a)。

如果您使用这种方法,可能会发现比预期更多的软件包使用了静态库。nettle-2.4 在其默认的仅静态配置中就是这种情况:它被 GnuTLS-3.0.19 所需,同时也被链接到使用 GnuTLS 的软件包中,例如 glib-networking-2.32.3

许多软件包将其部分通用函数放入静态库中,该静态库仅由该软件包内的程序使用,并且关键在于,该库 不会 作为独立库安装。这些内部库不是问题——如果为了修复错误或漏洞而需要重新构建该软件包,没有其他东西会链接到它们。

当 BLFS 提及系统库时,指的是库的共享版本。某些软件包(例如 Firefox-140.12.0ghostscript-10.07.1)在其构建树中捆绑了许多其他库。它们所带的版本通常比系统中使用的版本旧,因此可能包含错误——有时开发人员会费心修复其包含库中的错误,有时则不会。

有时,决定使用系统库很容易。其他时候,可能需要您更改系统版本(例如,用于 Firefox-140.12.0libpng-1.6.58)。偶尔,某个软件包会附带一个旧库,并且无法再链接到当前版本,但可以链接到较旧的版本。在这种情况下,BLFS 通常只会使用附带的版本。有时包含的库不再单独开发,或者其上游现在与软件包的上游相同,并且您没有其他软件包会使用它。在这些情况下,即使您通常更倾向于使用系统库,也会被引导使用包含的库。