%general-entities; ]> 软件包管理 软件包管理是一个经常被人请求添加到 LFS 手册中的内容。 包管理器可以跟踪软件包安装的文件, 这样就可以很容易地删除或者更新软件包。 和二进制程序、库文件一样,包管理器也会处理软件包安装的配置文件。 在您开始想入非非的时候,不! —— 本节不会讨论或推荐任何特定的包管理器。 本节对软件包管理的流行技术及其工作原理进行综述,对您来说, 完美的包管理器可能是以下的某个技术,或者可能是几个技术的结合。 本节还将简要提到在升级软件包时可能遇到的问题。 LFS 或 BLFS 不采用任何包管理器的原因有: 处理包管理器会偏离本书的重点目标 —— 讲述如何构建 Linux 系统。 存在多种软件包管理的解决方案,它们各有优缺点, 很难找到一种让所有读者满意的方案。 在 LFS 的 Hints Project 中有许多关于软件包管理的提示,您可以在其中查找, 看看有没有符合您需要的方案。 升级问题 包管理器可以在软件包新版本发布后容易地完成升级。一般来说, 使用 LFS 或者 BLFS 手册中的指令即可升级软件包。 下面是您在升级时必须注意的重点,特别是升级正在运行的系统时。 如果需要升级 Glibc (例如从 Glibc-2.19 升级到 Glibc-2.20), 最安全的方法是重新构建 LFS 。尽管您或许 能按依赖顺序重新构建所有软件包,但我们不推荐这样做。 如果更新了一个包含共享库的软件包,而且共享库的名称发生改变, 那么所有动态链接到这个库的软件包都需要重新编译, 以链接到新版本的库。 (注意,软件包的版本和共享库的名称没有关系。) 例如,如果一个软件包 foo-1.2.3 安装了名为 libfoo.so.1 的共享库,您把该软件包升级到了新版本 foo-1.2.4,它安装了名为 libfoo.so.2 的共享库。那么,所有链接到 libfoo.so.1 的软件包都要重新编译以链接到 libfoo.so.2。 注意,在重新编译这些软件包之前,您不能删除旧版本的库。 译注 文中的共享库的名称应解释为 ELF 文件中的 DT_SONAME,即 ldd 命令在箭头前输出的内容。 根据语义化版本所述, 共享库主版本号的递增表示 API 与之前版本不兼容, 故主版本号往往被包含在共享库名称中,例如 libfoo.so.2 中的 2 。 软件包管理技术 以下是几种常见的软件包管理技术,在决定使用某种包管理器前, 请研读这些技术,特别是要了解特定技术的不足。 这都在我的脑袋里! 您没有看错,这是一种包管理技术。有些人觉得不需要管理软件包, 因为他们十分了解软件包,知道每个软件包安装了什么文件。 有的用户则计划每次有软件包发生变动时就重新构建系统, 所以不需要管理软件包。 安装到独立目录 这是一种最简单的软件包管理方式, 它不需要任何额外的软件来控制软件包的安装。 例如,软件包 foo-1.1 将会被安装在 /usr/pkg/foo-1.1, 然后创建一个到该目录的符号链接 /usr/pkg/foo。 在安装新版本 foo-1.2 的时候,把它安装到 /usr/pkg/foo-1.2, 然后把之前的符号链接替换,使其链接到新版本。 PATHLD_LIBRARY_PATHMANPATHINFOPATHCPPFLAGS 等环境变量需要被扩充,以包含 /usr/pkg/foo。一旦软件包的数量较多, 这种架构就会变得无法管理。 软链接风格的软件包管理 这是前一种软件包管理技术的变种。 将各个软件包同样安装在独立的目录中,但不建立目录的软链接, 而是把其中的每个文件链接到 /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 中。 基于时间戳的管理 在这个方案中,安装一个软件包之前,为它创建一个时间戳文件。 在安装后,用一行简单的 find 命令, 加上正确的参数,就能生成安装日志, 包含在时间戳文件创建以后安装的所有文件。 有一个采用这个方案的包管理器叫做 install-log 。 尽管这种方式很简单,但它有两个缺点。如果在安装过程中, 某些文件没有以当前时间作为时间戳安装,它们就不能被包管理器跟踪。 另外,只有每次只安装一个软件包时才能使用这种技术, 如果在两个终端中同时安装两个不同的软件包, 它们的安装日志就不可靠了。 追踪安装脚本 在这种方式中,安装脚本执行的命令被记录下来。 有两种技术可以进行记录: 在安装前设置 LD_PRELOAD 环境变量, 将其指向一个库以在安装过程中预加载它。在安装过程中, 这个库将自身附加在cpinstallmv 等可执行文件上, 跟踪修改文件系统的系统调用。如果要使用这种方法, 所有需要跟踪的可执行文件必须是动态链接的,且没有设定 suid 和 sgid 位。预加载动态库可能在安装过程中导致不希望的副作用, 因此建议先进行一些测试,以确保包管理器不会造成破坏, 并且记录了所有应该记录的文件。 第二种技术是使用 strace, 它能够记录安装脚本执行过程中的所有系统调用。 创建软件包存档 在这种架构中,软件包被伪装安装到一个独立的目录树中, 就像软链接风格的软件包管理那样。 在安装后,使用被安装的文件创建一个软件包存档, 它可以被用来在本地机器甚至其他机器上安装该软件包。 大多数商业发行版的包管理器采用这种策略,例如 RPM (值得一提的是,它被 Linux Standard Base 规则 所要求),pkg-utils、 Debian 的 apt、Gentoo 的 Portage 系统)等。 LFS Hint 中的一条提示描述了如何为 LFS 系统适用这种管理方式: 创建包含依赖关系信息的软件包文件十分复杂,超过了 LFS 的范畴。 Slackware 使用一个基于 tar 的系统创建软件包档案。和更复杂的包管理器不同, 该系统有意地没有涉及软件包依赖关系。 如果想了解 Slackware 包管理器的详细信息,阅读 基于用户的软件包管理 这种架构是 LFS 特有的,由 Matthias Benkmann 提出,可以在 Hints Project 查阅。 在该架构中,每个软件包都由一个单独的用户安装到标准位置。 只要检查文件所有者,就能找出属于一个软件包的所有文件。 它的优缺点十分复杂,不适合在本节讨论,详细信息请阅读 在多个系统上部署 LFS LFS 系统的一项优势是,没有依赖于磁盘系统中文件位置的文件。 将构建好的 LFS 系统复制到另一台具有相同硬件架构的计算机很简单, 只要用 tar 命令把包含根目录的 LFS 分区打包 (未压缩的情况下,一个基本的 LFS 系统需要 250 MB), 然后通过网络传输或者刻成光盘,复制到新的系统上,再展开即可。 这时,个别配置文件需要修改,可能需要更新的配置文件有: /etc/hosts, /etc/fstab, /etc/passwd, /etc/group, /etc/shadow, 以及 /etc/ld.so.conf /etc/shadow, /etc/ld.so.conf, /etc/sysconfig/rc.site, /etc/sysconfig/network, 以及 /etc/sysconfig/ifconfig.eth0 由于系统硬件和原始内核配置的区别, 可能需要为新系统重新配置并构建内核。 有一些报告反映称, 在架构相近但不完全一致的计算机之间拷贝 LFS 系统时出现问题。 例如,Intel 系统使用的指令集和 AMD 处理器不完全相同, 且较新的处理器可能包含旧处理器没有的指令。 译注 据译者所知,LFS 中默认进行机器相关优化的软件包有 GMP-&gmp-version; 和 libffi-$libffi-version。 在编译它们时遵循构建指示中的 注意 框,即可构建通用的库文件。 然后注意整个构建过程不要使用 选项, 构建出的 LFS 系统就应该可以拷贝到架构相近 (uname -m 输出相同结果) 的机器上。 最后,按照 中的指示, 为新系统配置引导加载器。