6.3. 包管理

经常有人要求向 LFS 中加入包管理。包管理器允许跟踪文件的安装,使得移除和升级软件包变得容易。包管理器不仅会处理二进制和库文件,还会处理配置文件的安装。不要纠结——这一节既不会讲解也不会推荐任何一个包管理器。我们只是概述一下热门的技巧及其工作方式。对你来说完美的包管理器可能就在其中或者是其中几种的组合。这一节简要提及升级软件包时可能出现的问题。

LFS 或者 BLFS 中不提及包管理器的原因包括:

有关包管理有一些提示。访问 提示 看看能否满足你的需求。

6.3.1. 升级问题

包管理器使得在新版本发布时升级更简单。通常可以使用 LFS 和 BLFS 中的指令来升级到新版本。以下是在升级软件包,尤其是在运行的系统上如此做时需要注意的事项。

  • 如果工具链软件包之一 (Glibc、GCC 或 Binutils) 需要升级到一个新的小版本,重新构建 LFS 会更安全。虽然你可能能够通过按照依赖关系顺序重新构建所有软件包来解决问题,我们并不推荐这种方式。例如要升级 glibc-2.2.x 到 glibc-2.3.x,重新构建更加安全。对于微小版本升级,简单地重新安装通常就能工作,但是只是通常。例如将 glibc-2.3.4 升级为 glibc-2.3.5 通常不会产生任何问题。

  • 如果一个包含共享库的软件包升级了并且库的名称改变,那么所有动态链接到这个库的软件包都要重新编译以链接到新的库。(注意软件包版本和库名称之间并没有必然联系。) 例如,假设软件包 foo-1.2.3 安装的共享库名为 libfoo.so.1。其新版本 foo-1.2.4 将安装的共享库名为 libfoo.so.2。这种情况下所有动态链接到 libfoo.so.1 的软件包都要重新编译以链接到 libfoo.so.2。注意,在所有的依赖软件包重新编译完成之前你不应该移除先前的库。

6.3.2. 包管理技巧

以下是一些常见的包管理技巧。在选择包管理器之前,了解一下不同技巧,尤其是它们的缺点。

6.3.2.1. 我了然于胸!

是的,这也是一种包管理技巧。有些人因为非常了解软件包,知道每个包安装了什么文件所以不需要包管理器。还有一些用户也不需要包管理,因为他们打算在有软件包改动的时候重新构建整个系统。

6.3.2.2. 安装到分立目录中

这是一种简单的包管理方式,不需要额外的软件包来管理安装。每个软件包安装于一个独立的目录中。例如,软件包 foo-1.1 安装于 /usr/pkg/foo-1.1 中,并且创建一个由 /usr/pkg/foo/usr/pkg/foo-1.1 的符号链接。安装新的版本 foo-1.2 时,它安装于 /usr/pkg/foo-1.2 中,之前的符号链接被替换成到新版本的符号链接。

环境变量,比如 PATHLD_LIBRARY_PATHMANPATHINFOPATHCPPFLAGS 需要扩展以包括 /usr/pkg/foo。如果软件包很多,这种方法就很难管理。

6.3.2.3. 符号链接式包管理

这是前一种包管理技巧的一种变化。每个软件包安装的方式与前一种方式相同,但并不建立符号链接而是链接到 /usr 目录中。这样就不必扩展环境变量。虽然符号链接可以由用户创建,仍然有很多包管理器使用这种方式工作。其中比较流行的包括 Stow、Epkg、Graft和 Depot。

因为安装需要伪装,所以软件包认为被安装到了 /usr 却实际被安装到了 /usr/pkg 目录中。以这种方式安装通常不是什么普通的任务。例如要安装 libfoo-1.1 这个软件包。以下指令可能无法正确安装之:

./configure --prefix=/usr/pkg/libfoo/1.1
make
make install

安装能够工作,但是依赖的软件包可能不会像你期待的那样链接到 libfoo。如果你编译一个链接到 libfoo 的软件包,你会发现它链接到 /usr/pkg/libfoo/1.1/lib/libfoo.so.1 而不是你期待的 /usr/lib/libfoo.so.1。正确的方式是使用 DESTDIR 策略伪装该软件包的安装。这种方式如此工作:

./configure --prefix=/usr
make
make DESTDIR=/usr/pkg/libfoo/1.1 install

多数软件包都支持这种方式,但也存在不支持的。对于不支持的软件包,你可以手动安装之,但将这种麻烦的软件包安装至 /opt 中会更简单。

6.3.2.4. 基于时间戳

在这种技巧中,软件包安装之前先给一个文件打上时间戳。安装过后简单地使用 find 命令的恰当选项就可以创建此时间戳文件创建之后安装的所有文件的日志。install-log 就是用这种方式写成的包管理器。

虽然这种方式具有操作简单的优势,它也有两个缺点。如果在安装过程中有某些文件不是以当前时间作为时间戳安装的,这些文件就不会被包管理器追踪。另外,这种方式只能在每次安装一个软件包的条件下使用。如果在两个不同的控制台上同时安装两个软件包,日志就会不可靠。

6.3.2.5. 追踪安装脚本

在这种方法记录安装脚本运行的操作。有两种技巧可以使用:

可以设置 LD_PRELOAD 环境变量指向一个在安装之前预加载的库。在安装过程中,这个库将自己与多个可执行文件,如 cpinstallmv,来跟踪修改文件系统的系统调用。要让这种方式工作,所有的可执行文件都要不带 suid 和 sgid 位动态链接。预加载库可能在安装过程中产生你不想要的副作用。所以我们推荐进行一些测试来确保包管理器不会影响正常工作并记录所有的对应文件。

第二种技巧就是使用 strace,它会记录安装脚本执行过程中所有的系统调用。

6.3.2.6. 创建软件包归档

在这种方式中,软件包安装被伪装到一个独立的目录,与符号链接式包管理相同。在安装过后,用安装的文件创建一个软件包归档。这个归档可以用于安装这个软件包到本机甚至其他计算机。

这种方式被多数商业发行版中的包管理器应用。以这种方式工作的包管理器包括 RPM (被 Linux 基础标准顺带要求)、pkg-utils、Debian 的 apt 和 Gentoo 的 Portage 系统。有关如何在 LFS 系统中应用这种包管理方式的提示位于 http://www.linuxfromscratch.org/hints/downloads/files/fakeroot.txt

创建包含依赖关系信息的软件包文件是很复杂的,在 LFS 的考虑范围之外。

Slackware 使用一种基于 tar 的软件包归档系统。这个系统并没有像复杂的包管理器那样处理软件包依赖关系。有关 Slackware 包管理的细节,参考 http://www.slackbook.org/html/package-management.html

6.3.2.7. 基于用户管理

这种方式是 LFS 特有的,由 Matthias Benkmann 提出,在 Hints 工程中可用。这种方式要求每个软件包都作为独立的用户安装到标准位置。属于某一个软件包的文件可以通过检查用户 ID 来简单识别。这种方法的特点和缺陷过于复杂,在本章中不详细列举。其细节请参考 http://www.linuxfromscratch.org/hints/downloads/files/more_control_and_pkg_man.txt

6.3.3. 在多台计算机上部署 LFS

LFS 系统的一大优势就是没有文件依赖在磁盘系统中的位置。复制 LFS 系统到另一台架构相似的计算机上只要用 tar 打包包含根目录的 LFS 分区 (基础 LFS 副本压缩之前大约 250 MB),并通过网络或光盘复制到新系统上解压缩即可。然后要更改几个配置文件。要更新的配置文件包括: /etc/hosts/etc/fstab/etc/passwd/etc/group/etc/shadow/etc/ld.so.conf/etc/sysconfig/rc.site/etc/sysconfig/network/etc/sysconfig/ifconfig.eth0

可能还要给新系统构建一个新内核,这取决于原有内核的配置和系统硬件之间的差异。

最后要通过第 8.4 节 “Using GRUB to Set Up the Boot Process”使新系统能够启动。