一、前言

对于普通用户来说,安装一个程序,都可以比较简单的通过双击鼠标或者一条命令就可以完成,但如果没有包管理器,一切操作由用户自行完成,那么过程大概需要下载源码、编译、汇编、链接、将各类配置文件分门别类的存放,过程中需要人为指定大量参数,才能保证日后运行时某些需要的特性可以使用,并且用户也需要清楚的知道安装进度到了哪一步。这还仅仅是安装,那么日后的卸载呢?更新呢?人为操作步骤繁琐不说,出错的几率也非常大。所以就出现了包管理器,它可以将安装步骤简化到仅用一条命令,并且还将各类配置文件分发到正确路径,日后更新或者删除还可以准确的追踪每一个文件。

二、RPM包管理器

由红帽开发,全称为RPM is Package Manager

1. 命名方式

任何一个rpm包都是由它人拿到源代码编译制作的,所以rpm包命名方式和源码包相关

  • 源码包的命名方式:name-VERSION.tar.gz
    version由三部分组成:major.minor.release,也就是主版本号.次版本号.发行号
  • rpm包的命名方式:name-VERSION-ARCH.rpm
    version:同样继承源码包的major.minor.release
    ARCH由三部分组成:release.os.arch,这里的release为rpm的编译版本

示例:

  • 源码包名称:zlib-1.2.7.tar.gz
  • rpm包名称:zlib-1.2.7-13.el7.i686.rpm

2. 拆包的概念

如果一个程序包可以提供十种功能,大部分人都只用其中的五种,而一小部分人需要用到全部的功能,那么编译的时候就可以将一个包拆成多个包发行,让用户自行选择安装,这样不但可以节约空间,更重要的是,也免受未安装功能的漏洞侵害

3. 一个程序包的构成

  • 文件清单:记录这个程序包中的文件清单
  • 安装或卸载时运行的脚本:程序包安装或卸载时需要完成的预先配置工作或卸载后配置工作,都记录在这些脚本中

4. 包管理器数据库

4.1 功用

  • 记录每个程序包的名称及版本
  • 程序包的依赖关系
  • 程序包的功能性说明
  • 安装生成的歌文件路径及每个文件的校验码信息

4.2 管理相关

  • 路径:/var/lib/rpm
  • 初始化或重建:rpm {--initdb|--rebuilddb},通过读取每个程序包的元数据信息完成重建
    --initdb:如果事先不存在,则新建,否则不执行任何操作
    --rebuilddb:无论存在与否,直接重新创建数据库

5. 获取程序包的途径

  • 系统发行版的安装光盘或官方发行版服务器,光盘中的程序包是一般都是经过充分市场验证的包,但是时效性较差,而发行版的官方服务器访问速度较差,可以访问国内的镜像服务器,如阿里镜像:http://mirrors.aliyun.com、sohu镜像:http://mirrors.sohu.com网易镜像:http://mirrors.163.com
  • 项目官方:前往某个项目的官方服务器
  • 第三方组织的服务器:Fedora-EPEL、http://pkgs.org http://rpmfind.net http://rpm.pbone.net
  • 自行制作

6. 命令格式

安装命令格式:rpm {-i|--install} [install-options] PACKAGE_FILE ...

option
-v:详细信息
-vv:显示更多的调试信息
-h:以#显示程序包管理执行进度
install-options
--test:测试安装,但实际不安装
--nodeps:忽略依赖关系
--replacepkgs:重新安装,有可能会覆盖原先的配置文件
--ignoreos:忽略程序包适用的系统版本
--nodigest:不检查程序包的完整性
--nosignature:不检查包程序包的来源合法性
--noscripts:不执行脚本,包括安装前、后脚本,卸载前、后脚本
--nopre:不执行安装前脚本
--nopost:不执行安装后脚本
--nopreun:不执行卸载前脚本
--nopostun:不执行卸载后脚本
--notriggers:不执行触发器
--oldpackage:升级时实现降级的

升级命令格式

  • rpm {-U|--upgrade} [install-options] PACKAGE_FILE ...:如果安装有旧版程序包,则升级,如果指定不存在旧版程序包则安装
  • rpm {-F|--freshen} [install-options] PACKAGE_FILE ...:安装有旧版程序包则升级,如果不存在旧版程序包,则不执行操作
install-options
--oldpackage:降级,适用旧版程序包替换新版程序包
--force:强制操作,此命令个在程序包安装时也可以使用

tips:

  • 不要对内核做升级操作:linux内核4.0之前的内核升级完成都需要重新启动,如果升级失败了,重启根本无法完成。幸好linux支持多内核版本并存,所以直接安装新版本内核即可
  • 如果源程序包的配置文件安装 后曾被修改,升级时,新版本提供的同一个配置文件不会直接覆盖老版本的配置文件,而把新版本的文件重命名为FILENAME.rpmnew后保留

查询命令格式:rpm {-q|--query} [select-options] [query-options]

select-options
-a:全部安装的包
-g:查询指定包组安装的程序包
-p:对尚未安装的程序包做查询
-f:查询指定文件由哪个包安装生成
-g:查询指定包组的程序包
-p:如果想查询为安装的程序包,可以使用-p选项并结合其他选项来查询为安装的程序包信息
query-options
--changelog:查询指定rpm包的修改日志
-c:查询程序包的配置文件
--conflicts:查询程序包与哪个包冲突
-d:查询程序包生成的文档
-i:查询程序包的详细信息
-L:仅列出指定包的许可证信息
-l:查询指定程序包生成的所有文件
--scripts:查询程序包提供的脚本
-R:查询指定程序包所依赖的能力
--provides:列出指定程序包所提供的能力

卸载命令格式:rpm -e [--allmatches] [--justdb] [--nodeps] [--noscripts] [--notriggers] [--test] PACKAGE_NAME ...

--allmatches
--justdb
--nodeps:忽略依赖关系,强行卸载
--noscripts:卸载是不执行脚本
--notriggers
--test:测试卸载,不真实执行

校验命令格式:rpm {-V|--verify} [select-options] [verify-options]
此命令是用来校验以生成的文件与程序包数据库中的留存信息,这样就可以确定那个文件发生修改了,校验的内容包括文件的大小、权限、属主、属组等信息

verify-options
--nodeps:不检查依赖信息
--nodigest:不检查完整性信息
--nofiles
--noconfig:不检查配置文件
--noghost
--noscripts:不校验脚本文件
--nosignature:不检查来源合法性
--nolinkto
--nofiledigest (formerly --nomd5)
--nosize:不校验大小
--nouser:不校验属主
--nogroup:不校验属组
--nomtime:不校验修改时间
--nomode:不校验权限
--nordev
输出信息:
S file Size differs:文件大小不同
M Mode differs:权限不同
5 digest differs:md5码不同
D Device major/minor number mismatch:主次设备号不匹配
L 路径不匹配
U User ownership differs:属主不匹配
G Group ownership differs:属组不匹配
T mTime differs:修改时间不匹配
P caPabilities differ:能力不匹配

7. 程序包的来源合法性及完整性验证

互联网中的很多程序包都是个人制作,为了避免被内置很多后门,所以尽量选项目官方站点或项目站点下载程序包。不过,即便是从官方站点的下载的程序包,也有可能在传输过程中被篡改,所以验证包的来源合法性及完整性就尤为重要,可以使用命令导入官方提供的公钥信息进行验证。比如安装光盘中的,就有验证光盘中的程序包的公钥。

  • 导入公钥的命令:rpm --import /path/to/pubfile

三、 yum

由yellow dog研发,全称为Yellowdog Update Modifier

1. 配置文件

  • /etc/yum.conf:为所有仓库提供配置公共配置
cachedir:
keepcache:本地缓存的文件要不要保存
debuglevel:调试级别
logfile:日志文件位置
exactarch:精确匹配程序包适用的平台
obsoletes:
gpgcheck:检查来源合法性
plugins:插件机制
installonly_limit:同时安装程序包个数
bugtracker_url:追踪bug时的url
distroverpkg:
  • /etc/yum.repo.d/*.repo:为仓库的指向提供配置
[repositoryID]:仓库的名称,需要唯一
name:名称,尽可能为仓库功能的完成描述
baseurl:仓库路径指向,可以使用url://、http://、file:///,可以同时指向多个
mirrorlist:指向一个文件路径,其文件中可以指向多个baseurl
enabled:是否启用仓库
gpgcheck:是否校验
repo_gpgcheck:是否校验仓库元数据信息
gpgkey:校验公钥位置
enabledgroups:是否以允许在此仓库上基于组来安装包,默认为1
failovermethod:故障转移方法,如果baseurl指定多个,其中一个无法连接后,如何挑选下一个url,可指定为轮训或优先级
keepalive:是否使用保持连接功能
bandwidth:指定带宽
password:密码
username:有些服务器需要指定账号和密码
cost:指定仓库开销,默认为1000
  • 配置文件中的变量
$releasever:当前OS的发行版的主版本号
$arch:平台
$basearch:基础平台
$YUM0-$YUM9:用户自定义变量

2. 命令

命令格式:yum [options] [command] [package ...]

option
-y:自动回答yes
-q:静默模式
--disablerepo=repoid:临时禁用仓库
--enablerepo=repoid:临时启用仓库
--noplugins:禁用插件
--nogpgcheck:禁止检查gpgcheck
command:
install package1 [package2]:安装程序包
update [package1] [package2]:升级程序包
check-update:检查有哪些升级包可用
remove | erase package1 [package2]:卸载程序包
autoremove [package1] [...]
list [ all | glob | available | updates | install | extras]:显示程序包
@anaconda:在安装系统时安装的
available:表示没安装的包
updates:可用的升级包
extras:额外的包
obsoletes:废弃的包
info:查看包的简要信息
provides:查看指定特性或文件由哪个包提供
clean [ ]
packages:清理程序包
metadata:元数据
expire-cache:过期缓存
rpmdb:rpm数据库
plugins:插件
all:所有
makecache [fast]:生成缓存
group[ ]:包组工具
install:安装包组
update:升级包组
list:显示包组
remove:删除包组
info:包组信息
mandatory packages:必须安装的包
default packages:默认安装的包
optional packages:可选的包
search string1 [string2]:搜索包含字符串的包
localinstall rpmfile1 [rpmfile2]:安装本地包,自动解决依赖关系
localupdate rpmfile1 [rpmfile2]:升级本地包,自动解决依赖关系
reinstall package1 [package2]:重新安装
downgrade package1 [package2]:降级安装
deplist package1 [package2]:显示程序包的依赖关系
repolist [all|enabled|disabled]:显示仓库列表
history [ ]:查看系统yum事务
info
list
packages-list
packages-info
summary:摘要信息
addon-info
redo
undo
rollback
new
sync
stats:统计数据

四、程序包的编译安装

1. 程序为什么需要编译

你有没有想过我们只是想简单的运行一个程序,为什么需要这么繁琐,什么预处理、编译、汇编、链接之类的操作。比如我想运行Nginx,那么你直接给我一个nginx的程序,复制过去就能运行不就得了,整那么多干嘛?要想解释清楚这个问题,那么就需要扯的比较远了。下面我试着将这个问题讲清楚。
结合下图,一层一层的说,从下至上:

  • 硬件:我曾经看过一本书《穿越计算机的迷雾》(不过没读完),里面阐述了计算机为什么要用二进制、如何通过逻辑门(与门、或门、非门、同或门、异或门等)实现加减乘除计算。从宏观角度看一颗cpu,上面的一个针脚就是一个逻辑门,通过控制各种组合的针脚通电、放电来完成计算工作。那么问题就是,虽然它们都可以完成计算操作,但是不同厂商计算逻辑肯定是不同的,比如A说我们针脚1通电表示1,B说我们针脚2通电表示1,并且这些计算逻辑因为是商业机密的原因,肯定也不可能互相分享。这些通电放电的操作我们称之为指令集,想调用的必须通过二进制指令调用(叫做硬件规格或机器语言),但是对于大多数人来说,0101010的代码无异于天书,于是在cpu生产完成后,都会由厂商附加一套略微高级的调用接口,我们称之为微码编程接口或汇编语言,汇编语言至少大多数人能看懂了,但是任然很底层。如果你想在纯硬件上运行一个Nginx,其实是可以实现的,要么使用机器语言编写Nginx,要么使用汇编语言编写Nginx。以后呢,如何发布网页?如何添加图片,对于通篇的机器语言,能完成格式控制的人微乎其微,维护成本太高了,即便说你拥有这个能力,但其中也有一个无法避免的问题,就是这个程序局限性太高,它只能运行在某个厂商硬件的调用接口上,因为想运行在B厂商的硬件上,还需要从新学B的硬件调用接口。既然硬件级别不好编写程序,那么我们就去内核级别看看。
  • 内核:内核在硬件级别之上,它将底层的硬件差异抹平了,可以把他理解为一个翻译,底层的不同硬件理解为不同国家。于是我想使用任何功能只需要和翻译交流就行了,由翻译去·调用底层的真实硬件。具体硬件支持哪些功能呢?就要由这个翻译提前准备好,等待程序前来调用,这些封装好并提供给用户调用的接口,我们就称为系统库(system call),但是翻译多了,也面临同样的问题,第一,各操作系统system call调用逻辑不同,第二,system call任然比较底层。
  • 库:为解决系统调用过于底层的问题,于是它就再次将系统调用封装,向上再抽象一层,输出库调用接口。类似现实生活中的牛奶和奶牛,系统调用为奶牛,如果你想喝牛奶,你还需要自己去挤,而库调用直接就是牛奶。库调用不是必须的,有些牛人依旧可以面对系统调用来写程序,而且基于系统调用写的程序效率要更好,但是话说回来,写大型程序所需要的周期就会变长,库调用依然面临着调用方式不统一的问题,于是就出现了一个组织,它对于库的标准定义了一个规范,叫做POSIX规范(Portable Operating System可移植操作系统规范,后面的IX完全就是为了致敬Linux、Unix故意加上的),从而,使得写程序又进一步简化了。
  • 应用级虚拟化:既然库调用这么好,为什么还要有应用级虚拟化呢,因为使用库调用写的程序,必须精心编排它的程序逻辑,比如内存,运行程序前需要申请内存,运行完成后需要释放内存,这些都让人为来操作的话,就会带来很大的误操作风险,于是诞生了应用级虚拟化,大多数被称为解释器或者虚拟机的东西,这样就有省了很多步骤,还用内存举例,只用关心申请内存,而内存回收工作由虚拟机自动完成。并且它还有一个优点,就是他自己是一个虚拟机,不用考虑程序运行时的ABI不同的问题。

上述的层次仅解决的编程的难易程度(除应用级虚拟化外),换句话说这仅仅是从程序员的角度考虑的,但程序要想运行必须是二进制格式的,这就要从程序运行的角度考虑了。程序运行必须是二进制格式的,也就是必须从文本格式源码转换为目标平台的二进制代码。没转换前,没问题,拿着源码在哪里编译都可以,但一旦要运行,编译之后,那编译后的机器语言也仅仅适用于目标平台了。导致这种结果是因为他们的ABI不同,而程序员面对的编程接口叫API。

  • WINE/Cywin:它的功用就是解决Windows和Linux的ABI不同诞生的,使得编译后的程序也可以跨平台运行。比如有个程序A,只提供了Windows版本,但你又想在Linux平台上运行,厂商又不可能提供源码,这就可以在Linux上装一个WINE,它可以模拟Windows库调用接口、路径搜索等功能。
    在这里插入图片描述

2.编译安装的三步骤

  • ./configure
    通过选项传递参数,指定启用特性、安装路径等,执行时会参考用户指定及Makefile.in生成makefile文件
    检查依赖的外部环境
  • make:根据makefile文件,构建应用程序
  • make install

五、 关于库的一些命令

  • 查看二进制程序所依赖的库文件
    使用命令ldd /PATH/TO/BINARY_FILE
  • 管理及查看本机装载的库文件
    使用命令ldconfig -p查看本机内存所装载的库文件
    使用命令ldconf重新生成缓存
  • 配置文件
    /etc/ld.so.conf
    /etc/ld.so.conf.d/*.conf
  • 缓存文件:/etc/ld.so.cache