ITEEDU

6.3. 包管理

对于 LFS BOOK,包管理通常被请求加进去。 一个包管理器允许跟踪文件的安装, 使删除或升级软件包变得简单。 包管理器会处理配置文件以及二进制文件和库文件的安装。 在你开始惊讶之前, NO--在这一部分里,我们不会讨论或者是推荐任何一个特别的包管理器。 我们讲述的是一些流行的技术,以及他 们是怎么工作的。 对于你来说, 一个完美的包管理器可能在这些技术之中, 也可能是两个或更多。 这一部分简明的描述了当升级软件包的时候会出现的几个问题。

LFS 和 BLFS 中没有涉及包管理器的几个原因:

  • 把精力拿来做包管理器,就偏离了我们的LFS的初衷:指导如果构建一个Linux系统。

  • 包管理有很多解决方案,每一个都有优点和缺点。任何一个都很难令所有读者都满意。

有一些hints是关于包管理。可以查看 Hints Project 来选择一个适合你的。

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 目录,前面的符号链接也被指向新版本的符号链接取代。

一些环境变量,像 PATH, LD_LIBRARY_PATH, MANPATH, INFOPATHCPPFLAGS 都需要增加 /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 环境变量会设置成指向一个被提前加载的库。 在安装的过程中, 这个库就会跟踪正在安装的包,通过给自己附加上各种可执行性的动作,像 cp, install, mv, 来跟踪那些修改文件系统系统调用。为了让这种方法正常的工作, 所有的可执行文件需要 都是动态链接的且没有设置 suid 和 sgid 位。 预先加载库可能会产生一些讨厌的副作用。 因此, 建议做一些测试, 来确保包管理器不会破坏任何东西, 并且能够记录所有适当的文件。

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

6.3.2.6. 创建包的归档

在这种方案中,包被假装安装到不同的目录树下,就像符号连接风格的包管理描述的。安装之后, 系统就会使用安装的文件创建一个包的归档。这个归档可以在本机上安装包, 甚至可以在其他机器上安装包。

这种方法被大多数商业发行版的包管理器采用。 采用这种方法的包管理例子有RPM (顺便提一句, 是 Linux Standard Base Specification 需要的), pkg-utils, Debian 的 apt, 和 Gentoo 的 Portage 系统。 有一个描述在 LFS 系统中如何应用这样的包管理器的 hint,请参考 http://www.linuxfromscratch.org/hints/downloads/files/fakeroot.txt

创建包含依赖信息的包文件很复杂,并且超出了 LFS 的范围。

Slackware 使用 tar 基础系统作为包归档。 这个系统不像更复杂的包管理器一样处理包依赖。 关于Slackware 的包管理细节,请参考 http://www.slackbook.org/html/package-management.php.

6.3.2.7. 基于用户的管理

这种方案是 LFS 特有的,是由 Matthias Benkmann 提出的,可以参考 Hints Project。 在这种方案里,每一个包都是以不同的用户安装到标准的目录里。通过检验 user ID 可以很容易的标识一个软件包。 这种方法的特点和缺点非常复杂,这里就不描述了。详细内容参见 http://www.linuxfromscratch.org/hints/downloads/files/more_control_ and_pkg_man.txt.

6.3.3. 在多系统上部署LFS

LFS系统的一个优点是, 任何文件都不依赖其在磁盘系统上的位置。 将LFS系统克隆到另一个与 基础系统体系结构相似的计算机上,和在包含root目录的LFS分区上使用 tar (一个非压缩的 LFS 基础系统大约250M), 通过网络传输或CD-ROM 拷贝文件到新系统并展开它一样简单。 这时,一些配置问题件必须要更改。可能需要升级的配置文件包括: /etc/hosts, /etc/fstab, /etc/passwd, /etc/group, /etc/shadow, /etc/ld.so.conf, /etc/scsi_id.config, /etc/sysconfig/network/etc/sysconfig/network-devices/ifconfig.eth0/ipv4

为了使这个新系统适合不同的系统硬件和原始内核配置,我们可能需要编译一个定制的内核。

最后,新系统必须参照 Section 8.4, “使用 GRUB 配置启动过程” 配置成可启动。