关于 RPM 包依赖的思考
RPM 的依赖性是个很让人头疼的问题。每次系统安装完成过后,想再装一个软件包,敲下回车之前,都会心惊胆战,生怕跳出个依赖性错误。而在一个依赖性错误的背后,可能还牵连着更多的依赖性。这个问题让很多 Linux 用户望而生畏。
RPM 包的依赖关系说起来是简单的因为它所谓的依赖关系仅仅是一个个简单的如“要安装 A,需要提前准备好 a1, a2, a3”这样的条件。我们在安装前可以通过一个简单的 rpm 命令来查询一个包的依赖关系。
# rpm -q --requires -p wireshark-1.2.2-1.fc12.i686.rpm
/sbin/ldconfig
config(wireshark) = 1.2.2-1.fc12
libc.so.6
...
libcom_err.so.2
...
libpcap.so.1
...
libsmi.so.2
...
python(abi) = 2.6
...
RPM 的依赖性同时又是繁琐的,因为它的依赖关系里只包含了 A 需要 a1, a2 和 a3 的信息,并没有包含 a1, a2 和 a3 是由哪个包来提供。对于如 python 这样直接给出依赖包名,我们可以轻松地找到 python-xxx.rpm;对于如 libpcap.so.1 这样熟悉的库,我们知道它对应 libpcap-xxx.rpm;对于一部分不熟悉的,如 libsmi.so.2 这样的依赖库,我们也可以找到恰好与该库同名的 libsmi-xxx.rpm。但是对于 libglib-2.0.so.0 这样的库,你会很容易“猜”出它是来自于 glib2 软件包的吗?正是因为这样的喋喋不休,以及对应依赖包的含糊性,使得用户对 RPM 的依赖性产生了一定的畏惧心理。
那么怎么确定一个库由哪个包提供呢?对于已安装的包,可以用通过下面的方式来查询:
# rpm -q --whatprovides libpcap.so.1
libpcap-1.0.0-4.20090922gite154e2.fc12.i686
但是对于在安装过程中存在依赖的库,显示这种方法是不可行的。在一台独立的系统里,这个答案甚至是无解的。某个库包含在哪里,只能遍历安装包,在每个包查询“我提供了哪些东西”的才能找到。
下面是通过安装盘中找 libglib-2.0.so.0 由哪个包提供的例子,用时 21 秒,着实不短。如果用这种方法查找 libz.so.1,需要数分钟才能完成。
# mount /dev/hdc /mnt/cdrom
mount: block device /dev/hdc is write-protected, mounting read-only
# cd /mnt/cdrom/Server
# time for i in `ls -1`;
do
if rpm -q --provides -p $i | grep -q libglib-2.0.so.0
then
echo $i
break
fi
done
glib2-2.22.2-2.fc12.i686.rpm
real 0m21.804s
user 0m3.946s
sys 0m14.396s
yum 对 RPM 包的处理是一个典型的“空间换时间”过程。通过对源中所有 RPM 包的预处理,生成所有安装包关于包含文件,依赖,冲突等信息的索引,并且以 sqlite 格式存放在 /var/cache/yum 对应目录下面。
基于 yum 索引查找 libglib-2.0.so.0 由哪个包提供的例子如下:
# time echo "select packages.name from packages inner join provides
#on packages.pkgKey = provides.pkgKey where provides.name = 'libglib-2.0.so.0';" |
#sqlite3 /var/cache/yum/i386/12/dvd/primary.sqlite glib2
real 0m0.008s
user 0m0.003s
sys 0m0.005s
同样的查询,用索引数据来处理,只需要约 1/100 秒。数十兆的空间,换来的是成百上千倍时间效率的提升。
多年来,RPM 关于依赖性的原理并没有大的变化,只是经由这么一包装,让大家觉得依赖性逐渐不再是一个大的问题了。