概述:我们知道使用源代码进行软件编译可以具有定制化的设置,但对于Linux distribution的发行商来说,则有软件管理不易的问题,毕竟不是每个人对于操作系统都非常的熟悉,不是每个人都会进行源代码编译的,因此如果能够将软件现在相同的硬件与操作系统上编译好才发行的话,如果再加上简易的安装/删除/管理等机制,则对于软件管理会简单的多,因此有了我们目前最流行的RPM和YUM软件包管理神器,本节我们先介绍RPM,让我们一睹为快吧!
一、什么是RPM
RPM全名“RedHat Packgae Manger”简称则为RPM。它是以一种数据库记录方式将你所需要的软件安装到你的Linux系统的一套管理机制。
RPM最大的优点就是将你要安装的软件先编译过,并且打包成为RPM机制的安装包,通过包装好的软件里头默认数据库记录这个软件要安装的时候必须具备的依赖关系,当你安装软件包时,RPM会先依照软件里面的数据库查询Linux主机的依赖属性软件是否满足,若满足则予以安装,不满足则不予安装。这样以来我们可以总结它的优势在于:
1、RPM内容已经编译过程序与设置文件等数据,可以让用户免除重新编译的困扰
2、RPM在安装之前,会先检查系统的硬盘容量、操作系统的版本等,可以避免文件的错误安装
3、RPM文件本身提供软件版本信息、依赖属性软件名称、软件用途说明、软件所含文件等的信息,便于了解软件
4、RPM管理的方式使用数据库记录RPM文件的相关参数,便于升级、删除、查询与验证
同时我们也会发现这种软件管理机制的问题:
1、软件安装的环境必须与打包的环境需求一致
2、需要满足软件的依赖性要求
3、反安装时需要特别注意,最底层的软件不可先删除,否则可能造成整个系统的问题
二、RPM包名详解
name-VERSION-release.arch.rpm
例: bash-4.2.46-19.el7.x86_64.rpm
VERSION: major.minor.release (软件版本)
release: release.OS(发布的操作系统)
常见的arch(硬件平台):
x86: i386, i486, i586, i686(32位)
x86_64: x64, x86_64, amd64(64位)
powerpc: ppc
跟平台无关: noarch
包:分类和拆包
Application-VERSION-ARCH.rpm: 主包
Application-devel-VERSION-ARCH.rpm 开发子包
Application-utils-VERSION-ARHC.rpm 其它子包
Application-libs-VERSION-ARHC.rpm 其它子包
包之间:可能存在依赖关系,甚至循环依赖
解决依赖包管理工具:
yum: rpm包管理器的前端工具
apt-get: deb包管理器前端工具
zypper: suse上的rpm前端管理工具
dnf: Fedora 18+ rpm包管理器前端管理工具
三、RPM属性依赖的解决方式:YUM在线升级
我们知道安装RPM软件包经常会提醒你,少这个包少那个包,这都是软件互相的依赖性导致的,这也是RPM最头疼的事,那么有没有办法解决呢?前面不是谈到RPM会记录软件的依赖属性的相关数据吗?那么我们要是能将这些依赖关系属性软件先列表,在有软件需要安装的时候先到这个列表去查找,同时与系统内已经安装的软件相比较,没有安装的依赖软件一口气全部给安装了,不就一下解决了所谓的依赖性问题了吗?那么有没有这种软件包管理机制?当然有,这就是YUM机制。
我们先来简单了解YUM管理机制,后续会加以详细介绍。Centos会发布软件到YUM服务器内,然后分析这些软件的依赖性问题,将软件的记录信息写下来。然后再将这些信息分析后记录成软件相关性的清单列表。这些列表数据和软件所在的位置我们可以称为YUM仓库(repository)。当客户端有软件安装需求时,客户端主机会主动向网络上的yum服务器的仓库网址下载清单列表,然后通过清单列表的数据与本机RPM数据库已存在的软件数据相比较,就能够一口气安装所有需要具有依赖性的软件。整个流程我们可以使用下面的示意图表示:
当客户端有升级安装需求时,yum会向yum仓库要求清单更新,等到清单跟新到本机的/var/cache/yum里面后,接下来更新时就以用到这个本机清单与本机的RPM数据库进行比较在,这样就知道该下载哪些软件。然后yum就会跑到仓库的服务器下载所需要的软件,然后再通过RPM机制开始安装软件。这就是整个流程,因此YUM最终还是需要RPM的,它是最根本的,因此我们必须得掌握,接下来我们继续了解RPM。
四、RPM软件管理程序:rpm
1、RPM默认安装的路径
一般来说,RPM类型的文件在安装的时候,会先读取文件内记载的设置参数内容,然后将数据用来比较Linux系统的环境,以找出是否有属性依赖关系的软件没有安装的问题。若环境检查合格了,那么RPM文件就开始安装到你的Linux主机上。安装完毕后,该软件的相关的信息就会被写入/var/lib/rpm目录下面的数据库文件中。上年这个目录非常重要。因为我们有任何软件升级需求,版本之间的比较也就是来自这个数据库,而如果你想要查询系统已经安装的软件,也是根据这个查询的。
软件内的文件到底安装在哪里呢?当然根据你的文件系统有关了,通常为
下面我们针对每个RPM的相关命令进行说明。
2、RPM安装(install)
rpm {-i|–install} [install-options] PACKAGE_FILE…
-i:install(安装)
-v:verbose(查看详细的安装信息)
-h:安装信息栏显示安装进度
-vv:更详细的信息显示
通常我们使用:rpm -ivh PACKAGE_FILE …
[install-options]
–test: 测试安装,但不真正执行安装; dry run模式
–nodeps:忽略依赖关系
–replacepkgs | replacefiles
前者重新安装后者直接覆盖安装
–nosignature: 不检查来源合法性
–nodigest:不检查包完整性
–noscipts:不执行程序包脚本片断
%pre: 安装前脚本; –nopre
%post: 安装后脚本; –nopost
%preun: 卸载前脚本; –nopreun
%postun: 卸载后脚本; –nopostun
3、RPM升级(upgrade)
rpm {-U|–upgrade} [install-options] PACKAGE_FILE…
rpm {-F|–freshen} [install-options] PACKAGE_FILE…
upgrade:安装有旧版程序包,则“升级”如果不存在旧版程序包,则“安装”
freshen:安装有旧版程序包,则“升级”如果不存在旧版程序包,则不执行升级操作
rpm -Uvh PACKAGE_FILE …
rpm -Fvh PACKAGE_FILE …
–oldpackage:降级
–force: 强行升级,这个参数相当于–replacepkgs和–replacefiles的结合
升级注意事项:
(1) 不要对内核做升级操作; Linux支持多内核版本并存,因此,对直接安装新版本内核。
(2) 如果原程序包的配置文件安装后曾被修改,升级时,新版本的提供的同一个配置文件并不会直接覆盖老版本的配置文件,而把新版本的文件重命名(FILENAME.rpmnew)后保留。
4、RPM查询(query)
rpm {-q|–query} [select-options] [query-options]
[select-options]
-a: 所有包
-f: 查看指定的文件由哪个程序包安装生成
-p rpmfile:针对尚未安装的程序包文件做查询操作;
–whatprovides CAPABILITY:查询指定的CAPABILITY
由哪个包所提供
–whatrequires CAPABILITY:查询指定的CAPABILITY被
哪个包所依赖
rpm2cpio 包文件|cpio –itv 预览包内文件
rpm2cpio 包文件|cpio –id “ *.conf” 释放包内文件
[query-options]
–changelog:查询rpm包的changelog
-c: 查询程序的配置文件
-d: 查询程序的文档
-i: information
-l: 查看指定的程序包安装后生成的所有文件;
–scripts:程序包自带的脚本片断
-R: 查询指定的程序包所依赖的CAPABILITY;
–provides: 列出指定程序包所提供的CAPABILITY;
我们经常要使用的组合选项:
-q:仅查询,后面接软件名称查看是否安装
-qa:列出所有的已经安装在本机系统上的所有软件名称
-qi:后面接软件名称,列出该软件的详细信息
-ql:后面接软件名称,列出该软件所有的文件与目录
-qc:后面接软件名称,列出该软件的配置文件
-qd:后面接软件名称,列出该软件的帮助文件
-qR:后面接软件名称,列出该软件所依赖软件所含的文件
-qf:由后面接文件,找出该文件属于哪一个已安装的软件
-qp:注意用途仅在于找出某个RPM文件内的信息,而非已安装的软件!
4、RPM卸载与重建数据库(erase/rebuilddb)
rpm {-e|–erase} [–allmatches] [–nodeps] [–noscripts] [–notriggers] [–test] PACKAGE_NAME …
通常使用:rpm -e packge_name
由于RPM文件经常会安装、升级、卸载等操作,某些操作或许会导致RPM数库/var/lib/rpm的损坏,因此我们使用–rebuilddb这个参数来重建数据库。
rpm {–initdb|–rebuilddb}
initdb: 初始化,如果事先不存在数据库,则新建之。否则,不执行任何操作。
rebuilddb:重建,无论当前存在与否,直接重新创建数据库。
5、RPM验证与数字证书(Verify/Signature)
rpm {-V|–verify} [select-options] [verify-options]
-V:后面加软件名称,若该软件所含的文件被修改过,则会被检查出来
-Va:列出系统上面可能被改动过的软件所包含的文件
-Vp:后面加文件名称,列出该软件可能被改动的文件
-Vf:后面接文件名称,列出某个文件是否被修改过
使用上述参数可以查出来文件名前面会出现一些不熟悉的单个字母或者一些字母,我们来了解下这些字母的含义。
S file Size differs (文件的容量大小是否被修改)
M Mode differs (includes permissions and file type)
文件的类型或者文件的属性是否被改变
5 digest (formerly MD5 sum) differs
MD5指的是一种指纹码的内容已经不同
D Device major/minor number mismatch
设备的主次代码已经改变
L readLink(2) path mismatch(Link路径已经发生改变)
U User ownership differs(文件所有者发生改变)
G Group ownership differs(文件所属组发生改变)
T mTime differs(文件的修改时间已经发生改变)
P capabilities differ
至于上面的那个c是什么东西呢?C代表“configure”,也就是文件类型,文件类型有下面几类:
c:配置文件
d:文档
g:ghost文件
l:授权文件
r:自述文件
谈完了软件的验证后,不知道你有没有发现一个问题,那就是验证只能验证软件内的信息与数据库里面的信息而已,如果该软件提供的数据本身就有问题呢?那你使用验证的手段就无法确定该软件的正确性,那么怎么解决呢?我们可以用过数字证书来检验软件来源的。
数字证书就像自己的签名一样,我们的软件开发商所推出的软件会有一个自己证书系统,只是这个证书被数字化了,厂商可以使用数字证书系统产生一个专属与该软件的证书,并将该证书的公钥发布,当你要安装一个RPM文件是:
a、首先你必须要先安装原厂发布的公钥文件
b、实际安装原厂RPM软件是,rpm命令会读取RPM文件的证书信息,与本机系统内的证书信息比较
c、若证书相同则予以安装,若找不到相关的证书信息,则给予警告且停止安装。
根据上述说明,我们也会知道首先必须要安装原厂释放的GPG数字证书的公钥文件,Centos7的数字证书位于:/etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
我们也可以使用find命令查找文件名的关键词进行查找
find / -name ‘*GPG-KEY*’
导入所需要公钥:
rpm -K|checksig rpmfile 检查包的完整性和签名
rpm –import /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
CentOS 7发行版光盘提供: RPM-GPG-KEY-CentOS-7
rpm -qa gpg-pubkey*
本文仅为Linux的软件包管理器神器之一&RPM的简单介绍,其中有不不对的地方请各位老司机予以指正,后续我们还会继续介绍YUM在线升级以及编译安装方法。
五、赠送脚本小练习
1、写一个服务脚本/root/bin/testsrv.sh,完成如下要求
(1) 脚本可接受参数: start, stop, restart, status
(2) 如果参数非此四者之一,提示使用格式后报错退出
(3) 如是start:则创建/var/lock/subsys/SCRIPT_NAME, 并显示“启动成功”
考虑:如果事先已经启动过一次,该如何处理?
(4) 如是stop:则删除/var/lock/subsys/SCRIPT_NAME, 并显示“停止完成”
考虑:如果事先已然停止过了,该如何处理?
(5) 如是restart,则先stop, 再start
考虑:如果本来没有start,如何处理?
(6) 如是status, 则如果/var/lock/subsys/SCRIPT_NAME文件存在,则显示
“ SCRIPT_NAME is running…”
如果/var/lock/subsys/SCRIPT_NAME文件不存在,则显示“ SCRIPT_NAME
is stopped…”
其中: SCRIPT_NAME为当前脚本名
#!/bin/bash #作者:薛帅 #版本:1.0 #时间:2016-08-21 #脚本功能描述: #写一个服务脚本/root/bin/testsrv.sh,完成如下要求 #(1) 脚本可接受参数: start, stop, restart, status #(2) 如果参数非此四者之一,提示使用格式后报错退出 #(3) 如是start:则创建/var/lock/subsys/SCRIPT_NAME, 并显示“启动成功” #考虑:如果事先已经启动过一次,该如何处理? #(4) 如是stop:则删除/var/lock/subsys/SCRIPT_NAME, 并显示“停止完成” #考虑:如果事先已然停止过了,该如何处理? #(5) 如是restart,则先stop, 再start #考虑:如果本来没有start,如何处理? #(6) 如是status, 则如果/var/lock/subsys/SCRIPT_NAME文件存在,则显示 #“ SCRIPT_NAME is running...” #如果/var/lock/subsys/SCRIPT_NAME文件不存在,则显示“ SCRIPT_NAME #is stopped...” #其中: SCRIPT_NAME为当前脚本名 #================================================================ script_name=$(basename $0) servicefile=/var/lock/subsys/"$script_name" start() { if [ -f $servicefile ];then echo "服务已经启动了" else touch $servicefile echo "启动成功" fi } stop() { if [ -f $servicefile ];then rm -f $servicefile echo "停止完成" else echo "服务已经关闭了" fi } restart() { stop start } status() { if [ -f $servicefile ];then echo " SCRIPT_NAME is running..." else echo "SCRIPT_NAMEis stopped..." fi } #===========主程序================= PS3="根据上述提示,选择一个数字:" select menu in start stop restart status ;do case $menu in start) start ;; stop) stop ;; restart) restart ;; status) status ;; esac exit 5 done
执行结果如图所示:
2、编写一个脚本/root/bin/copycmd.sh
(1) 提示用户输入一个可执行命令名称;
(2) 获取此命令所依赖到的所有库文件列表
(3) 复制命令至某目标目录(例如/mnt/sysroot)下的对应路径下;
如: /bin/bash ==> /mnt/sysroot/bin/bash
/usr/bin/passwd ==> /mnt/sysroot/usr/bin/passwd
(4) 复制此命令依赖到的所有库文件至目标目录下的对应路径下:
如: /lib64/ld-linux-x86-64.so.2 ==> /mnt/sysroot/lib64/ldlinux-x86-64.so.2
(5)每次复制完成一个命令后,不要退出,而是提示用户键入新的要复制的命令,并重复完成上述功能;直到用户输入quit退出
#!/bin/bash #作者:薛帅 #版本:1.0 #时间:2016-8-21 #脚本功能描述:编写一个脚本/root/bin/copycmd.sh #(1) 提示用户输入一个可执行命令名称; #(2) 获取此命令所依赖到的所有库文件列表 #(3) 复制命令至某目标目录(例如/mnt/sysroot)下的对应路径下; #如: /bin/bash ==> /mnt/sysroot/bin/bash #/usr/bin/passwd ==> /mnt/sysroot/usr/bin/passwd #(4) 复制此命令依赖到的所有库文件至目标目录下的对应路径下: #如: /lib64/ld-linux-x86-64.so.2 ==> /mnt/sysroot/lib64/ldlinux-x86-64.so.2 #(5)每次复制完成一个命令后,不要退出,而是提示用户键入新的要复制的命 #令,并重复完成上述功能;直到用户输入quit退出 #================================================================= copyfile() { local dir=$(dirname $1) #取传递参数的目录名 local destdir=/mnt/sysroot$dir #由于没有该目录,因此定义一个目标的目录,留下面创建目录用 mkdir -p $destdir #创建以/mnt/sysroot下面是你要传递的目录 cp -a "$1" "$destdir" #复制你传递的目录文件到上面新建的目录里面 } #=========主函数================ while true;do read -p "请输入一个命令" mingling if [[ "$mingling" == "quit" ]];then echo "退出啦!" break else dir=$(which $mingling |grep "/.*") liblist=$(ldd $dir |grep -o "/lib64/.*"|cut -d" " -f1) copyfile $dir # 复制命令至某目标目录(例如/mnt/sysroot)下的对应路径下 for libdir in $liblist;do #循环将该命令的库文件一个一个复制到/mnt/sysroot/lib64/路径下 copyfile $libdir done fi done unset dir unset liblist unset mingling
执行结果如下图所示:
原创文章,作者:AndyIvanXue,如若转载,请注明出处:http://www.178linux.com/38773