CentOS 7上的程序管理:rpm、yum和源碼編譯安裝


簡介

在Linux的早期時代(也許吧?我猜的。也可能是Unix。),想要在系統上安裝一款應用程序,是比較復雜的。需要專業的人員自行獲取程序的源代碼,並且編譯安裝,這是非常的復雜且需要一定的專業功底的,這種方式叫做源碼編譯安裝(后面會描述)。

再后來就有人/組織將這個過程簡化了,他們將事先已經編譯好的軟件打包后,放到互聯網上供用戶下載。用戶下載適用於自己的操作系統和硬件平台的軟件包之后,只需要將其“解壓”,那么該軟件包所包含的各種文件(二進制程序文件、文檔文件、庫文件和配置文件等)就會基於包作者事先定義好的位置進入各自對應的目錄下了。在CentOS上,這種打包工具就叫做rpm,即rpm安裝

但是這種工具又存在一個缺陷,那就是它無法自動解決RPM包之間的依賴性。

假設安裝A包需要依賴於B包,安裝B包又要依賴於C包。

A --> B --> C

當你安裝A包的時候提示你要安裝B包,你安裝B包的時候又提示你要安裝C包,它無法自動去解決這個依賴關系。並且你還要自己去下載被依賴的B包和C包。

因此誕生了一種更高級的包管理器,用於解決上述的RPM的問題,在CentOS上,這種包管理器就叫做yum,即yum安裝。不過,在未來,可能是CentOS 8的時候,yum會被dnf工具所取代,在Fedora系統上,已經有dnf工具了。

在不同的Linux發行版中,這種軟件包的組織形式有點不同,見下表:

發行版 初級包管理器 高級包管理器 包名稱
RHEL系列:Red Hat、CentOS、Fedora rpm yum、dnf *.rpm
Debian、Ubuntu dpkg apt-get *.deb
S.u.S.E rpm zypper *.rpm

本文的講解順序是先講rpm安裝,再講yum安裝,最后說最復雜的源碼編譯安裝。

 

RPM

在當前的運維環境下,一般我們安裝某款軟件,會直接使用yum命令,而不會單獨去下載rpm包通過rpm命令來完成。

更多的是使用rpm命令去執行一下對程序包的查詢操作。

程序包的獲取

推薦從軟件的官方站點獲取,或者可以在一些比較大型的受信任的鏡像站點獲取。

  1. 系統發行版的光盤。
  2. 項目的官方站點。
    • www.zabbix.com
    • www.mysq.com
    • 等等。
  3. 開源鏡像站點及一些rpm包提供站點。
  4. 自己動手制作rpm包。

程序包的名稱

程序包(rpm包)名稱的大概格式如下。

NAME-VERSION-RELEASE.OS.ARCH.rpm

示例

perl-5.16.3-293.el7.x86_64.rpm

NAME:軟件的名稱,例如“perl”。

VERSION:軟件的版本,一般格式為“major.minor.release”,即主版本.次版本.發行版本,例如“5.16.3”。

RELEASE:程序包的發行版,發行版可以理解為發行的次數,依次往上疊加,區別於VERSION中的release,這里的RELEASE指的是程序包的發行版本,而不是軟件的。可能軟件的版本沒改變但是程序包再次制作發行了。例如“293”。

OS:Linux系統的大版本,CentOS 6為el6,CentOS 7為el7,el表示Enterprise Linux。

ARCH:硬件平台。例如i386,x86_64,amd64,ppc,noarch。noarch表示該程序包不受硬件平台的限制,可能該包均通過庫調用之類來實現自身功能。

主包與支包

程序除了以自身名稱命名的包以外,可能還存在一些其他的包用於提供與該程序相關的功能或者用於擴展該程序。

以PHP來舉例。

PHP的主包

php-5.4.16-46.el7.x86_64.rpm

PHP的支包

php-bcmath-5.4.16-46.el7.x86_64.rpm  # 提供了數學計算相關的PHP擴展庫。
php-cli-5.4.16-46.el7.x86_64.rpm  # 提供了PHP命令行程序/usr/bin/php。

安裝

基本語法如下。

rpm {-i|--install} [install-options] PACKAGE_FILE ...

-v:輸出詳細信息。

-vv:輸出更詳細的信息。

-h,--hash:hash marks輸出進度條,每個#表示2%的進度。

--test:測試安裝,檢查並報告依賴關系及沖突消息等,類似於干跑(--dry-run)。

--nodeps:忽略依賴關系,忽略依賴關系之后安裝的程序包,可能可以正常運行(比如依賴的只是一個文檔類的程序包),不建議這么做。

--replacepkgs:重新安裝,一般用於修改了程序包的某個文件無法復原的時候選擇的選項,如果重新安裝依然無法恢復修改的文件,可以事先刪除該文件再重新安裝。

--noscripts:禁用rpm包的相關腳本(scriptlet)。該選項等同於“--nopre --nopost --nopreun --nopostun”。

rpm包可以包含四種腳本:

  • preinstall:安裝過程開始之前運行的腳本,%pre,--nopre
  • postinstall:安裝過程完成之后運行的腳本,%post,--nopost
  • preuninstall:卸載過程開始之前運行的腳本,%preun,--nopreun
  • postuninstall:卸載過程完成之后運行的腳本,%postun,--nopostun

可以思考一下,比如我們安裝nginx或者php-fpm或者MySQL的時候,在系統中會產生與之對應的用於運行服務的用戶,那么這個創建用戶的操作,應該就是rpm包的腳本所實現的了。

--nosignature:不檢查包簽名信息,即不檢查包來源的合法性。

--nodigest:不檢查包摘要信息,即不檢查包完整性信息。

包來源合法性和包完整性是什么,在后面會描述,先知道該選項即可。

最常用的選項如下。

rpm -ivh PACKAGE_FILE ...
[root@C7 ~]# rpm -ivh zsh-5.0.2-31.el7.x86_64.rpm 
Preparing...                          ################################# [100%]
Updating / installing...
   1:zsh-5.0.2-31.el7                 ################################# [100%]

--test選項,也可能會用到。其他選項基本不會使用到。

[root@C7 ~]# rpm -ivh --test zsh-5.0.2-31.el7.x86_64.rpm 
Preparing...                          ################################# [100%]
    package zsh-5.0.2-31.el7.x86_64 is already installed

因為此前已經安裝過,所以我們通過測試運行,就可以看到會有已安裝的提示。

升級

rpm {-U|--upgrade} [install-options] PACKAGE_FILE ...
rpm {-F|--freshen} [install-options] PACKAGE_FILE ...

-U:表示升級或者安裝,即如果軟件此前未安裝,則可以直接安裝。

-F:明確表示只能升級,即如果軟件此前未安裝,則不會直接安裝。

一般也會結合-vh選項來使用。

rpm -Uvh PACKAGE_FILE ...
rpm -Fvh PACKAGE_FILE ...

--oldpackage:默認情況下,升級操作只可以升級到新版本,使用該選項的話,可執行降級操作。

--replacefiles:即便安裝操作會覆蓋其他程序包所安裝的文件,仍然安裝程序包。

--replacepkgs:即便程序包的某些文件已經安裝在系統上,仍然安裝程序包。

--force:等同於--replacepkgs、--replacefiles和--oldpackage。

網上下載了zsh 5.1.1版本的,進行升級實驗。

[root@C7 ~]# rpm -Uvh --test zsh-5.1-1.gf.el7.x86_64.rpm 
warning: zsh-5.1-1.gf.el7.x86_64.rpm: Header V4 RSA/SHA1 Signature, key ID da8b7718: NOKEY
Preparing...                          ################################# [100%]
[root@C7 ~]# rpm -Uvh zsh-5.1-1.gf.el7.x86_64.rpm
warning: zsh-5.1-1.gf.el7.x86_64.rpm: Header V4 RSA/SHA1 Signature, key ID da8b7718: NOKEY
Preparing...                          ################################# [100%]
Updating / installing...
   1:zsh-5.1-1.gf.el7                 ################################# [ 50%]
Cleaning up / removing...
   2:zsh-5.0.2-31.el7                 ################################# [100%]
[root@C7 ~]# rpm -qa | grep -i "zsh"
zsh-5.1-1.gf.el7.x86_64

升級新的包之后,會卸載舊的包。rpm -qa命令用於查看當前系統上安裝的程序包,下文會描述。

仔細看的話,可以發現有一行警告。

warning: zsh-5.1-1.gf.el7.x86_64.rpm: Header V4 RSA/SHA1 Signature, key ID da8b7718: NOKEY

這是因為rpm包的來源合法性和包完整性無法得到驗證,我們沒有提供包提供方的key導致的,后面會描述。

降級操作。

[root@C7 ~]# rpm -q zsh
zsh-5.1-1.gf.el7.x86_64
[root@C7 ~]# rpm -Uvh --oldpackage zsh-5.0.2-31.el7.x86_64.rpm 
Preparing...                          ################################# [100%]
Updating / installing...
   1:zsh-5.0.2-31.el7                 ################################# [ 50%]
Cleaning up / removing...
   2:zsh-5.1-1.gf.el7                 ################################# [100%]
[root@C7 ~]# rpm -q zsh
zsh-5.0.2-31.el7.x86_64

注意事項:

  • 不要對內核做升級操作;若想要使用新版本的內核,Linux支持多內核版本並存,因此直接安裝新版本內核即可。
  • 如果某原程序包的配置文件在安裝后曾被修改過,升級時,新版程序包提供的同一個配置文件不會覆蓋老版本已修改過的配置文件,而是把新版本的配置文件重命名(FILENAME.rpmnew)后提供。

卸載

rpm {-e|--erase} [--allmatches] [--nodeps] [--noscripts] [--test] PACKAGE_NAME ...

注意,在安裝和升級的過程中,rpm命令的操作對象都是rpm包文件的名稱(PACKAGE_FILE),例如“zsh-5.0.2-31.el7.x86_64.rpm”。

而卸載的時候,只需要rpm包名(PACKAGE_NAME)即可,例如“zsh”。

--allmatches:卸載所有匹配PACKAGE_NAME的所有版本的程序包。

我這里無法測試該選項,例如zsh,無論我升級還是降級,rpm都只會保留一個版本的zsh,因此我不知道如何測試該選項。

不過一般卸載所使用到的選項並不多,和安裝一樣,使用一些通用的選項即可。

[root@C7 ~]# rpm -evh zsh
Preparing...                          ################################# [100%]
Cleaning up / removing...
   1:zsh-5.0.2-31.el7                 ################################# [100%]
[root@C7 ~]# rpm -q zsh
package zsh is not installed

查詢

查詢才是rpm使用中最經常用到的操作。因為安裝、升級和卸載都可以使用yum命令來完成,而查詢我們則基本上是使用rpm命令。

rpm {-q|--query} [select-options] [query-options]

[select-options]

PACKAGE_NAME:查詢指定的程序包是否已安裝,若已安裝則會顯示其版本。

[root@C7 ~]# rpm -q bash
bash-4.2.46-30.el7.x86_64

-a, --all:查詢所有已安裝的程序包,可以結合管道+grep命令過濾。

[root@C7 ~]# rpm -qa | grep -i "bash"
bash-4.2.46-30.el7.x86_64
bash-completion-2.1-6.el7.noarch

-f, --file FILE:查詢出指定的FILE是哪個程序包的。

[root@C7 ~]# rpm -qf /bin/bash
bash-4.2.46-30.el7.x86_64

-g, --group GROUP:查詢某個程序包組包含哪些程序包。注意,這里的GROUP,並不是通過yum grouplist中所看到的程序包組名稱,而是通過-i選項所查詢到的包詳細信息中的GROUP!

例如我們常安裝的包組為Development Tools,就不是這里所指的GROUP。即便已安裝了該包組。

[root@C7 ~]# rpm -qg "Development Tools"
group Development Tools does not contain any packages
[root@C7 ~]# rpm -qi bash | grep -i "^group"
Group       : System Environment/Shells
[root@C7 ~]# rpm -qg "System Environment/Shells"
bash-4.2.46-30.el7.x86_64
tcsh-6.18.01-15.el7.x86_64

-p, --package PACKAGE_FILE:對未安裝的程序包執行查詢操作,查詢的內容與query-options相關。

  在query-options中,許多的選項可以用於查看已安裝的程序包的信息;

  但是如果我們擁有一個RPM包文件,但是不打算安裝它,想查看此RPM包所包含的文件;

  就可以使用命令“rpm -qpl PACKAGE_FILE”,這就是p選項的用法;

例如,假設我們本機沒有安裝zsh,那么我們就無法查詢其相關信息了。

[root@C7 ~]# rpm -qi zsh
package zsh is not installed

但如果我們有程序包文件的話,就可以借助-p選項來實現查詢的功能,只不過這里的命令參數就是PACKAGE_FILE而不是PACKAGE_NAME了。

[root@C7 ~]# rpm -qpi zsh-5.0.2-31.el7.x86_64.rpm

--whatprovides CAPABILITY:查詢所有包中具備某種能力(capability)的程序包。這里的能力指的是什么,就需要看底下的query-options中的--provides和-R選項了。

首先我們基於下文的選項,可以知道bash程序包提供了以下能力。

[root@C7 ~]# rpm -q --provides bash
/bin/bash
/bin/sh
bash = 4.2.46-30.el7
bash(x86-64) = 4.2.46-30.el7
config(bash) = 4.2.46-30.el7

以“/bin/bash”這個能力來舉例,我們可以查看哪些程序包提供了該能力。

[root@C7 ~]# rpm -q --whatprovides "/bin/bash"
bash-4.2.46-30.el7.x86_64

而如果能力是那種帶有等於號與版本信息的,應該忽略掉等於號和版本信息,只保留能力名稱,並且需要使用單引號或者雙引號。

[root@C7 ~]# rpm -q --whatprovides "bash(x86-64) = 4.2.46-30.el7"
no package provides bash(x86-64) = 4.2.46-30.el7
[root@C7 ~]# rpm -q --whatprovides "bash(x86-64)"
bash-4.2.46-30.el7.x86_64
[root@C7 ~]# rpm -q --whatprovides bash(x86-64)
-bash: syntax error near unexpected token `('
[root@C7 ~]# rpm -q --whatprovides 'bash(x86-64)'
bash-4.2.46-30.el7.x86_64

--whatrequires CAPABILITY:查詢所有包中需要某種能力(capability)的程序包。

[root@C7 ~]# rpm -q --whatrequires "/bin/bash"
nss-softokn-freebl-3.34.0-2.el7.x86_64
copy-jdk-configs-3.3-2.el7.noarch
iproute-4.11.0-14.el7.x86_64
xorg-x11-xinit-1.3.4-2.el7.x86_64
...

我猜測,rpm包應該就是基於包的能力(capability)來決定包與包之間的依賴關系的。

能力這塊,目前知道即可,我們並不需要手工去查詢來解決依賴關系,因為我們有yum工具。

[query-options]

--changelog:顯示程序包的變更日志(changelog),這里的變更日志指的是RPM程序包的,而非軟件源碼包的。程序包的changelog和軟件的changelog的區別可能在於,比如軟件源代碼可以不改變,但是程序包的打包方式改變了,例如部分文件的存放位置發生了修改。

[root@C7 ~]# rpm -q --changelog bash | head
* Mon Sep 25 2017 Siteshwar Vashisht <svashisht@redhat.com> - 4.2.46-30
- Check for multibyte characters in commands
  Resolves: #1487615

* Thu Aug 03 2017 Siteshwar Vashisht <svashisht@redhat.com> - 4.2.46-29
- Fix a pipe fd leak in process substitution
  Resolves: #1473245

* Tue Mar 07 2017 Kamil Dudka <kdudka@redhat.com - 4.2.46-28
- CVE-2016-9401 - Fix crash when '-' is passed as second sign to popd

-l, --list:查詢程序包所包含的所有文件(二進制程序文件、文檔文件、庫文件和配置文件等)。

[root@C7 ~]# rpm -ql bash
/etc/skel/.bash_logout
...
/usr/bin/alias
/usr/bin/bash
...
/usr/share/doc/bash-4.2.46
...
/usr/share/man/man1/..1.gz
...

-c, --configfiles:僅查詢程序包所包含的配置文件。

[root@C7 ~]# rpm -qc bash
/etc/skel/.bash_logout
/etc/skel/.bash_profile
/etc/skel/.bashrc

-d, --docfiles:僅查詢程序包所包含的文檔文件。

[root@C7 ~]# rpm -qd bash
/usr/share/doc/bash-4.2.46/COPYING
...
/usr/share/man/man1/..1.gz
...

-i, --info:查詢程序包的詳細信息(information)。這個是比較重要的信息!

[root@C7 ~]# rpm -qi bash
Name        : bash    # 程序包名稱。
Version     : 4.2.46    # 程序包版本。
Release     : 30.el7    # 程序包發行版,包含了OS。
Architecture: x86_64    # 程序包適用硬件平台。
Install Date: Thu 27 Sep 2018 03:50:45 PM CST    # 安裝日期。
Group       : System Environment/Shells    # 這里的group,就是上面“rpm -qg GROUP”中的group。
Size        : 3667709
License     : GPLv3+
Signature   : RSA/SHA256, Wed 25 Apr 2018 06:54:19 PM CST, Key ID 24c6a8a7f4a80eb5
Source RPM  : bash-4.2.46-30.el7.src.rpm    # RPM源碼包,和純源碼包又完全不同,后面會描述。
Build Date  : Wed 11 Apr 2018 08:55:22 AM CST
Build Host  : x86-01.bsys.centos.org
Relocations : (not relocatable)
Packager    : CentOS BuildSystem <http://bugs.centos.org>
Vendor      : CentOS    # 程序包制作商。
URL         : http://www.gnu.org/software/bash    # 軟件相關的官方站點。
Summary     : The GNU Bourne Again shell    # 軟件的簡要信息。
Description :    # 軟件的詳細信息。
The GNU Bourne Again shell (Bash) is a shell or command language
interpreter that is compatible with the Bourne shell (sh). Bash
incorporates useful features from the Korn shell (ksh) and the C shell
(csh). Most sh scripts can be run by bash without modification.

--provides:查詢程序包所提供的能力(capability)。

[root@C7 ~]# rpm -q --provides bash
/bin/bash
/bin/sh
bash = 4.2.46-30.el7
bash(x86-64) = 4.2.46-30.el7
config(bash) = 4.2.46-30.el7

-R, --requires:查詢程序包所依賴的能力(capability)。

[root@C7 ~]# rpm -qR bash
/bin/sh
config(bash) = 4.2.46-30.el7
libc.so.6()(64bit)
libc.so.6(GLIBC_2.11)(64bit)
libc.so.6(GLIBC_2.14)(64bit)
libc.so.6(GLIBC_2.15)(64bit)
libc.so.6(GLIBC_2.2.5)(64bit)
libc.so.6(GLIBC_2.3)(64bit)
libc.so.6(GLIBC_2.3.4)(64bit)
libc.so.6(GLIBC_2.4)(64bit)
libc.so.6(GLIBC_2.8)(64bit)
libdl.so.2()(64bit)
libdl.so.2(GLIBC_2.2.5)(64bit)
libtinfo.so.5()(64bit)
rpmlib(BuiltinLuaScripts) <= 4.2.2-1
rpmlib(CompressedFileNames) <= 3.0.4-1
rpmlib(FileDigests) <= 4.6.0-1
rpmlib(PayloadFilesHavePrefix) <= 4.0-1
rtld(GNU_HASH)
rpmlib(PayloadIsXz) <= 5.2-1

--scripts:查詢程序包在安裝和卸載過程中使用的特殊腳本程序。我自己查了bash和postfix程序包,發現可使用shell script和lua語言編寫。

[root@C7 ~]# rpm -q --scripts bash
postinstall scriptlet (using <lua>):
...
postuninstall scriptlet (using <lua>):
-- Run it only if we are uninstalling
...

包來源合法性與包完整性

當我們從網上下載了一個rpm包之后,如何確保這個rpm包就是該站點所提供的?如何確保rpm包沒有被第三方所篡改呢?

這就需要依賴於rpm包的來源合法性和包完整性驗證了。

來源合法性驗證依賴於數字簽名,包完整性驗證依賴於消息摘要算法。

rpm包實現包來源合法性和包完整性檢測的原理如下:

  1. 包制作者使用單向加密算法(比如md5)對包進行加密得到校驗碼(或者說是消息摘要);
  2. 包制作者使用自己的私鑰去加密校驗碼,並將加密后的校驗碼附加在包的尾部;
  3. 包使用者使用包制作者提供的公鑰對包尾部的加密后的校驗碼進行解密,如果解密成功,則證明包的來源是合法的(確實是包制作者本人發出的,這里涉及到非對稱加密的概念);
  4. 包使用者使用和包制作者相同的單向加密算法(比如md5)對包進行加密得到校驗碼,如果此校驗碼和剛才包使用者使用公鑰解密后得到的校驗碼一致,那么就證明了包的完整性(包在傳輸的過程中未被篡改)。

這里涉及到加密與解密的這些理論知識,將來應該會寫一篇文章來詳細闡述。

如果我們想檢測一個rpm,那么我們就需要一個公鑰文件,這個文件,一般是包制作方會提供,在系統鏡像文件中也會有。

系統安裝好之后,在“/etc/pki/rpm-gpg/”目錄下會有公鑰(密鑰有公鑰和私鑰兩種)文件。

[root@C7 ~]# ls -l /etc/pki/rpm-gpg/
total 16
-rw-r--r--. 1 root root 1690 Apr 29  2018 RPM-GPG-KEY-CentOS-7
-rw-r--r--. 1 root root 1004 Apr 29  2018 RPM-GPG-KEY-CentOS-Debug-7
-rw-r--r--. 1 root root 1690 Apr 29  2018 RPM-GPG-KEY-CentOS-Testing-7
-rw-r--r--  1 root root 1662 Oct  3  2017 RPM-GPG-KEY-EPEL-7

當我們有了密鑰后,就可以將其導入rpm,用作后續的校驗操作。

CentOS 6和CentOS 7操作有些許不同,因為6和7默認的rpm版本不一樣。6的寫法,在7上也可以使用。

[root@C6 ~]# rpm -q rpm
rpm-4.8.0-55.el6.x86_64
[root@C7 ~]# rpm -q rpm
rpm-4.11.3-35.el7.x86_64
CentOS 6:rpm --import PUBKEY ...
CentOS 7:rpmkeys --import PUBKEY ...

有了key之后,就可以對程序包進行檢測了。

CentOS 6:rpm {-K|--checksig} [--nosignature] [--nodigest] PACKAGE_FILE ...
CentOS 7:rpmkeys {-K|--checksig} PACKAGE_FILE ...

zsh-5.0.2版本的,此前從阿里雲的CentOS 7鏡像下載,因為系統自帶了key,因此可以檢測通過。而zsh-5.1.1則沒有key,因此無法檢測。

[root@C7 ~]# rpmkeys -K zsh-5.0.2-31.el7.x86_64.rpm 
zsh-5.0.2-31.el7.x86_64.rpm: rsa sha1 (md5) pgp md5 OK
[root@C7 ~]# rpmkeys -K zsh-5.1-1.gf.el7.x86_64.rpm 
zsh-5.1-1.gf.el7.x86_64.rpm: RSA sha1 ((MD5) PGP) md5 NOT OK (MISSING KEYS: (MD5) PGP#da8b7718)

除了主動檢查包以外,每次在安裝/升級等操作的時候,rpm也會自動去檢查的,例如上文我們看到的那個警告信息。

warning: zsh-5.1-1.gf.el7.x86_64.rpm: Header V4 RSA/SHA1 Signature, key ID da8b7718: NOKEY

相關的幫助。

CentOS 6man rpm
CentOS 7man rpmkeys

校驗

rpm {-V|--verify} [select-options] [verify-options]

校驗操作會將已安裝的文件的信息和rpm數據庫的元數據中記錄的信息做比較,這些信息包括了文件大小、摘要(digest)、權限、類型、屬主和屬組等。如果這些信息有改變,那么就會將其顯示出來。

如果在安裝的時候沒有安裝該文件的話,則會忽略未安裝的文件。例如,在安裝的時候使用了--excludedocs選項,那么在校驗的時候就會忽略文檔類型的文件。

[root@C7 ~]# rpm -V bash
[root@C7 ~]#

如果程序包中的文件均為改動過,那么就不會有任何的輸出。

接下來我們修改某個配置文件,加一行注釋;修改了版權文件的時間;修改cd程序文件的屬主和屬組。

[root@C7 ~]# vim /etc/skel/.bash_logout
[root@C7 ~]# touch /usr/share/doc/bash-4.2.46/COPYING
[root@C7 ~]# chown zwl:zwl /usr/bin/cd

再次校驗。

[root@C7 ~]# rpm -V bash
S.5....T.  c /etc/skel/.bash_logout
.....UG..    /usr/bin/cd
.......T.  d /usr/share/doc/bash-4.2.46/COPYING

在輸出的內容中,由三個部分構成。

第一個部分由9個字段構成。小數點“.”表示檢查沒問題。如果出現問號“?”的話,表示無法檢查,比如可能因為權限問題導致無法讀取文件信息。

  • S:Size,文件大小改變。
  • M:Mode,權限或者文件類型改變。
  • 5:消息摘要改變,一般是md5算法;消息摘要不同,意味着文件內容發生了改變。
  • D:Device,設備的設備號不匹配。
  • L:readLink(2)路徑不匹配。
  • U:User,屬主改變。
  • G:Group,屬組改變。
  • T:mTime,文件修改時間改變。
  • P:caPabilities,包能力改變。

第二個部分是屬性標記,可有可無的。

  • c:%config,配置文件。
  • d:%doc,文檔文件。
  • g:%ghost file (i.e. the file contents are not included in the package payload).
  • l:%license,許可證文件。
  • r:%readme,類似文檔文件。

RPM數據庫

rpm的數據庫位於目錄/var/lib/rpm/中,rpm基於這些數據庫的內容才可以完成它上述的功能。數據庫文件的格式是伯克利DB。

[root@C7 ~]# file /var/lib/rpm/*
/var/lib/rpm/Basenames:    Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Conflictname: Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Dirnames:     Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Group:        Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Installtid:   Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Name:         Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Obsoletename: Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Packages:     Berkeley DB (Hash, version 9, native byte-order)
/var/lib/rpm/Providename:  Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Requirename:  Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Sha1header:   Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Sigmd5:       Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Triggername:  Berkeley DB (Btree, version 9, native byte-order)
rpm {--initdb|--rebuilddb} [-v] [--dbpath DIRECTORY] [--root DIRECTORY]

--initdb:初始化數據庫,即創建一個新的,如果原本已經有的話,就不創建。

--rebuilddb:重建數據庫,一般如果數據庫損壞了,可使用。它會基於已安裝的包頭(package header)來重建數據庫索引。

--dbpath:指定數據庫的位置,默認是/var/lib/rpm/。

--root:指定rpm數據庫所基於的根文件系統路徑。默認應該是系統根“/”。注:該選項解釋不確定。

[root@C7 ~]# rpm --initdb -v --dbpath=/tmp/rpm_test
[root@C7 ~]# ls /tmp/rpm_test/
Basenames  Conflictname  __db.001  __db.002  __db.003  Dirnames  Group  Installtid  Name  Obsoletename  Packages  Providename  Requirename  Sha1header  Sigmd5  Triggername

有嘗試指定了--root選項的值為/usr或者/usr/local,均無法初始化出數據庫文件。

 

YUM

正如文章開頭所描述的,yum是一款基於rpm包的包管理器。相對於rpm,它自動幫我們解決了包下載以及包依賴性的問題。

yum是一款C/S架構的工具。客戶端即為本地的Linux端,需要安裝軟件的那一端;服務器端即為遠程倉庫(repository)端,也叫做yum源。

倉庫端除了存放眾多的RPM包以外,還存放了相關的元數據(應該是記錄包之間的依賴關系等)repodata目錄。

存放repodata目錄的地址,即為yum源地址,例如該示例中的“https://mirrors.aliyun.com/zabbix/zabbix/3.0/rhel/7/x86_64/”。

yum源的地址及倉庫的地址。地址可使用的協議如下。一般最常見的還是http協議,file協議用於指定本地yum源。

ftp://
http://
nfs://
file:///

YUM配置文件

在使用yum之前,我們必須正確配置好yum的配置文件,系統安裝好之后,默認就已經幫我們啟用了一些配置文件。

主配置文件:/etc/yum.conf,這里存放的是倉庫相關的通用配置[main]。

倉庫配置文件:/etc/yum.repos.d/*.repo,這里才是每一個倉庫所對應的配置文件,比如zabbix和MySQL可以存放在不同的配置文件中,zabbix.repo和mysql.repo。

一般來說,主配置文件,我們是不用去改動的,更多的是管理倉庫配置文件。

主配置

主配置文件中,一般只有一個[main]配置段,它管理的是全局配置。該配置段,也只能有一個。

我們來看下系統默認提供的。

[main]
cachedir=/var/cache/yum/$basearch/$releasever
keepcache=0
debuglevel=2
logfile=/var/log/yum.log
exactarch=1
obsoletes=1
gpgcheck=1
plugins=1
installonly_limit=5
bugtracker_url=http://bugs.centos.org/set_project.php?project_id=23&ref=http://bugs.centos.org/bug_report_page.php?category=yum
distroverpkg=centos-release

在一些表示“是”和“否”的配置中,會使用1(true)或者0(false)來表示。

cachedir:yum的緩存和數據庫存儲目錄。對於當前系統來說就是“/var/cache/yum/x86_64/7/”。

keepcache:在安裝成功后,是否保持頭和包的緩存。值為1或者0,默認是1(表示保持)。

debuglevel:debug消息輸出級別。范圍是0~10,默認是2。

logfile:日志文件位置,需要寫全路徑(含目錄名和文件名)。

exactarch:

obsoletes:只在更新/升級程序包時有效,應該是允許包含廢棄/陳舊(obsolete)數據包,在發行版升級時(例如CentOS 6 --> CentOS 7),比較有用。默認是1。

gpgcheck:是否執行GPG簽名檢測,默認是0。如果在[main]配置中設置了,那么它會應用於所有的倉庫中。目前還沒在man手冊中看到明確的說法,不過個人認為,如果[main]和倉庫的配置存在相同的配置項的話,那么在[main]中配置的會成為倉庫的默認配置;如果兩邊均配置了,則應該是以倉庫配置為准。

plugins:是否啟用插件,默認是0。插件用於擴展yum功能,我的系統默認有2個插件。

  fastestmirror:用於根據下載速度將yum源排序,然后選擇下載最快的yum源來使用。

  langpacks:條件式語言支持包,應該是和語言(中英文這類)相關。

installonlypkgs:程序包名稱列表。在該列表中的程序包,只可以被安裝,不可以被更新。一般都是內核相關的程序包,具體見man手冊。

installonly_limit:在installonlypkgs列表中,一次性同時安裝的程序包數量,默認是3。這個配置項應該是和“installonlyn”插件比較有關系。

bugtracker_url:應該是bug追蹤反饋的一個地址。

distroverpkg:這里配置的是一個rpm包的名字。一般是system-release(releasever)、redhat-release或者centos-release。對於我的系統來說,它是centos-release。這個包用來告訴yum我們所使用的Linux發行版是哪種,並且該包還提供了發行版的GPG密鑰、repo文件等。

倉庫配置

[repositoryid]
name=Some name for this repository
baseurl=url://path/to/repository/

repositoryid:倉庫的唯一標識符,一般是一個單詞。

name:倉庫的描述信息。

baseurl:倉庫的地址,也叫yum源地址。這個地址下必須有“repodata”目錄。URL可以有多個,但是官方不建議。URL也可以帶上HTTP的基本驗證信息。

baseurl=http://user:passwd@example.com/

enabled:是否啟用該倉庫。

gpgcheck:同理[main]配置段。

repo_gpgcheck:是否對倉庫的repodata執行GPG 簽名檢查。

gpgkey:指定GPG密鑰,如果此前沒有導入過的話,則yum會自動導入。

enablegroups:是否允許yum安裝程序包組,即yum groupinstall,默認為1。

failovermethod:當yum源聯系不上時,使用的故障轉移辦法。roundrobin表示隨機,注意不是輪詢;priority表示按順序。

username和password:yum源URL的基本驗證信息,可寫在URL中,或者[main]配置段,或者倉庫配置段。

cost:倉庫的開銷,開銷越小越優先。默認是1000。

變量

配置文件中是可以包含變量的。

$releasever:如果是CentOS 6的話,則該值為6;同理,CentOS 7的值為7。

$arch:系統的硬件平台(architecture),常見的有i386、i486、i586、x86_64等等。

$basearch:基礎硬件平台,例如如果系統是i486、i586之類的,則會顯示為i386。

$uuid:根據當前機器生成的唯一標識符,值位於/var/lib/yum/uuid文件中。

$YUM0-$YUM9:會被系統同名環境變量所取代,一般用於用戶自定義。

baseurl=http://mirror.centos.org/centos/$releasever/os/$basearch/
等同於
baseurl=http://mirror.centos.org/centos/7/os/x86_64/

YUM的命令

基本語法。

yum [options] [command] [package ...]

yum的具體功能,通過command來實現。

倉庫相關

repolist [all|enabled|disabled]

用於列出倉庫信息,默認只列出已啟用的。

[root@C7 ~]# yum repolist
Loaded plugins: fastestmirror, langpacks    # 載入yum插件
Loading mirror speeds from cached hostfile    # 插件輸出信息,這部分輸出后面會忽略
 * base: mirrors.aliyun.com
 * epel: mirrors.aliyun.com
 * extras: mirrors.huaweicloud.com
 * updates: mirrors.aliyun.com
repo id                                                                     repo name                                                                                                status    # 狀態,即程序包個數
!base/7/x86_64    # repo id前的感嘆號表示該倉庫的元數據過期了。                   CentOS-7 - Base                                                                                          10,019
!epel/x86_64                                                                Extra Packages for Enterprise Linux 7 - x86_64                                                           13,139
!extras/7/x86_64                                                            CentOS-7 - Extras                                                                                           413
!updates/7/x86_64                                                           CentOS-7 - Updates                                                                                        1,840

可以跟上repo id或者repo name來顯示對應的倉庫。

# yum repolist REPO_ID
# yum repolist REPO_NAME

可以通過-v選項或者repoinfo顯示倉庫詳細信息。

# yum repolist -v ...
# yum repoinfo ...

程序包列表

list [...]

默認情況下,列出所有的程序包,程序包名稱支持globbing風格。

yum list [all | glob_exp1] [glob_exp2] [...]

可以列出程序包名稱、具體的版本信息以及來自哪個倉庫。@REPO_ID(中的@)表示這是一個已安裝的包。

dwz.x86_64                             0.11-3.el7                      @base    
dyninst.x86_64                         9.3.1-1.el7                     @anaconda    # anaconda表示的應該是隨系統安裝的時候裝的。

列出未安裝但是可安裝的包。

yum list available [glob_exp1] [...]

列出所有可更新的包。

yum list updates [glob_exp1] [...]

更多的相關命令,查看“man yum”中的“LIST OPTIONS”。

程序包安裝

install package1 [package2] [...]

默認情況下,會安裝倉庫中最新版本的程序包。安裝對象,也可以直接是一個本地的rpm文件,當需要依賴其他包時,yum會自動解決。

程序包重裝

reinstall package1 [package2] [...]

程序包更新/升級

更新就是升級的意思。

update [package1] [package2] [...]

如果沒指明package的話,那么默認情況下,yum會更新所有已安裝的程序包。

如果在更新過程中,被依賴的程序包也需要更新,那么yum會更新;如果新增了被依賴的程序包,那么yum也會新安裝。

upgrade [package1] [package2] [...]

upgrade等同於“update --obsoletes”或者主配置文件的[main]中啟用了“obsoletes”。英文原意是“在計算中包含廢棄的包”,我猜測可能是保留這些廢棄的包吧。

可以在更新之前,查看有哪些程序包可以更新。

# yum check-update

除了升級以外,還可以降級。

downgrade package1 [package2] [...]

一般降級的時候,在包名中會指明版本號,如果沒指明的話,則安裝最新版的上一版。

程序包卸載

remove | erase package1 [package2] [...]

在卸載的同時,會將依賴於該程序包的程序包一並卸載。yum程序包本身位於protected_packages配置中,所以不會被誤卸載。

程序包能力

provides | whatprovides feature1 [feature2] [...]

這里的feature,應該等同於上文所說的包的能力(capability)。

根據能力,來查詢哪些包支持這些。

# yum provides "bash(x86-64)"

緩存相關

清理緩存。

clean [ packages | metadata | expire-cache | rpmdb | plugins | all ]

這里所清理的緩存,都是那些已啟用的倉庫,如果想清理所有(含未啟用的),請使用“--enablerepo='*'”選項。

我自己都嘗試清理了一下,應該都是緩存,所以下次運行的時候,yum還會從源中去獲取這些數據的,應該問題不大。

但是不要自己通過rm去刪除yum的緩存(“/var/cache/yum/”)。

重新生成緩存。會從服務器端下載並生成元數據。

makecache [fast]

如果傳遞了fast參數,那么就等同於運行了“yum clean expire-cache”。

程序包搜索

search string1 [string2] [...]

當你不知道程序包名稱的時候,可以使用該選項。

默認情況下,搜索包的name和summary字段(rpm -qi信息)。如果沒找着的話,會嘗試description和url字段。可通過傳遞all作為第一個參數來搜索全部字段。

# yum search all database

程序包依賴

deplist package1 [package2] [...]

會顯示程序包所依賴的能力,以及可提供該能力的對應的程序包。了解即可,yum可自行解決依賴關系。

程序包組相關

groupinstall group1 [group2] [...]
groupupdate group1 [group2] [...]
grouplist [hidden] [groupwildcard] [...]
groupremove group1 [group2] [...]
groupinfo group1 [...]

注意:上面的語法,是CentOS 6上的yum的,在CenTOS 7上會稍微有點不同。

了解即可,基本上會使用到的也就只有安裝而已。常見是安裝開發工具。

# yum group install "Development Tools"

其他的,詳見man手冊。

YUM的選項

--nogpgcheck:禁止進行gpgcheck。

-y, --assumeyes:自動回答yes安裝。

-q, --quiet:靜默模式。

--disablerepo=repoidglob:本次yum安裝禁用某個或某些(使用glob)倉庫。

--enablerepo=repoidglob:本次yum安裝啟用某個或某些(使用glob)倉庫。

--noplugins:禁用所有插件。

--disableplugin=plugin:禁用某些插件。

連接YUM源

光盤YUM源

我自己是在虛擬機上實現。首先就是將光盤插入機器中,對於我來說就是指定光盤ISO鏡像文件並且將其配置為“已連接”。

在系統中掛載,留意掛載的文件系統類型。

# mount -r -t iso9660 /dev/cdrom /media/cdrom/

配置repo文件。

# cat /etc/yum.repos.d/cdrom.repo 
[cdrom]
name=This is a yum repo from cdrom.
baseurl=file:///media/cdrom/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
enabled=1

查看倉庫信息。

[root@C7 ~]# yum repolist -v cdrom
...
pkgsack time: 0.011
Repo-id      : cdrom
Repo-name    : This is a yum repo from cdrom.
Repo-status  : enabled
Repo-revision: 1525380860
Repo-updated : Fri May  4 04:54:34 2018
Repo-pkgs    : 3,971
Repo-size    : 3.6 G
Repo-baseurl : file:///media/cdrom/
Repo-expire  : 21,600 second(s) (last: Tue May 14 11:16:02 2019)
  Filter     : read-only:present
Repo-filename: /etc/yum.repos.d/cdrom.repo

repolist: 3,971

如果該倉庫不使用了,那么先在倉庫配置文件中關閉倉庫啟用狀態,然后卸載(umount)文件系統,再然后使用eject命令彈出光盤驅動器。

# eject /dev/sr0

軟件YUM源

這里我以MariaDB為例,首先我是去找了阿里雲的鏡像站,跳轉到了MariaDB的官網,在對方所提供的Repository Configuration Tool中選擇了一些系統信息后,自動幫我生成了repo文件。

[root@C7 ~]# cat /etc/yum.repos.d/MariaDB.repo 
# MariaDB 10.3 CentOS repository list - created 2019-05-14 03:34 UTC
# http://downloads.mariadb.org/mariadb/repositories/
[mariadb]
name = MariaDB
baseurl = http://yum.mariadb.org/10.3/centos7-amd64
gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
gpgcheck=1

其他的軟件,也是類似的做法,要么去比較受信任的國內大型鏡像站點,要么去官方站點。

當我們想單獨查看已增加的倉庫中有哪些程序包時,可以使用該命令。

# yum repository-packages mariadb list

詳情可參考repository-packages子命令。

repository-packages <enabled-repoid> <install|remove|remove-or-reinstall|remove-or-distribution-synchronization> [package2] [...]

 

源碼編譯安裝

參考資料:

Linux下源代碼的編譯安裝入門

How to Install Software from Source Code… and Remove it Afterwards

RPM安裝,是使用別人事先制作好的rpm包。包中的二進制程序文件,是對方根據軟件需求方的硬件、系統等信息事先編譯好的,只能適用於那個系統、硬件的機器上。例如,64位CentOS 7系統上的rpm包,就無法應用於32位CentOS 6系統。

YUM安裝,由於是基於RPM實現的,所以也會有這個現象。

源代碼在撰寫的過程中,可能將軟件的所有功能都已經寫好了。但是,編譯的時候,一般是根據需求來編譯,不需要的功能是不會編譯入軟件中的。

如果通過rpm或者yum安裝的軟件,沒有我們所需要的功能(但是軟件的源代碼中是有該功能的),此時就需要用戶自行基於源碼編譯安裝了。

源碼編譯安裝,是一個有時候讓人覺得極其痛苦的過程,不同的Linux發行版,不同的軟件,組合起來在安裝的過程當中,會有不同的問題。加之本人對C語言源碼編譯的過程也不太了解,所以本文說的不一定正確。

引用中有一篇簡書的中文資料和一篇It's FOSS的英文資料可供參考,后者我沒看過,若將來想深入研究,可看。

源代碼編譯大致流程:
源代碼 --> 預處理 --> 編譯(gcc) --> 匯編 --> 鏈接 --> 執行

一般來說,Linux下的軟件大多數為C語言所編寫,所以使用的自動化構建工具一般是make。涉及的程序包為autoconf和automake,一般我們裝上Development Tools包組就會有。

# yum group install "Development Tools"

接下來就是軟件源代碼的獲取,一般是去官方網站,這里以memcached為例。獲取到的是.tar.gz格式的文件,解壓后,進入目錄中。

# tar -xzf memcached-1.5.14.tar.gz
# cd memcached-1.5.14/

進入目錄后,我們首先要找到相關的幫助文件,一般是大寫的INSTALL或者README,對本示例來說,是INSTALL和README.md。一般是文本文件,可直接查看。

這類文件肯定都是英文的了,不好閱讀。對於本示例來說,README.md文件還好,INSTALL文件就非常長了。一般來說,最簡單的步驟為如下。

# ./configure && make && make install

configure是一個POSIX兼容的shell腳本,運行該腳本可用於檢測軟件所需要的各類環境,例如是否有各種各樣的庫支持等。

可用於指定軟件文件的安裝位置,最常用的是--prefix選項。

--prefix=PREFIX
--prefix=/usr/local/memcached/

還有更加細粒度的針對不同類型的文件的安裝位置調整。

--bindir=DIR
--libexecdir=DIR
--libdir=DIR
--mandir=DIR
--docdir=DIR
...

可用於開啟或者關閉一些特性。

--disable-FEATURE
--enable-FEATURE[=ARG]

是否依賴某些程序包。一般是某些帶有lib或者devel字符串的包。

--with-PACKAGE[=ARG]
--without-PACKAGE

這些可詳見:

# ./configure --help

接下來就讓我們來實操一下,我們僅指定--prefix選項。源碼編譯基本最頭疼的地方就在第一步,因為可能會報很多錯誤。

# ./configure --prefix=/usr/local/memcached/

錯誤一。

checking for libevent directory... configure: error: libevent is required.  You can get it from http://www.monkey.org/~provos/libevent/

      If it's already installed, specify its path using --with-libevent=/dir/

通過yum命令我發現本機已安裝libevent程序包,但是沒有安裝libevent-devel,因此我們嘗試安裝后者。

# yum list *libevent*
# yum install libevent-devel

再次configure后,成功了。

注意:雖然報錯提示我們去官網下載libevent來安裝,但是能通過yum/rpm安裝的就盡量通過該方式安裝,盡量避免源碼編譯安裝。而且根據本人經驗,上文也說了,一般這種缺少庫之類的,都是缺少一些devel類或者lib類的包。

configure: creating ./config.status
config.status: creating Makefile
config.status: creating doc/Makefile
config.status: creating config.h
config.status: executing depfiles commands

config.status:保留有我們此前的編譯選項,直接運行該腳本應該或許可以再次生成相同的Makefile文件。

config.h:用於包含系統依賴定義。

Makefile:自動化構建所需要使用到的配置文件,運行完configure腳本后會根據模板文件Makefile.in生成。

接下來我們就可以執行make了。

# make

make是根據Makefile文件調用gcc等編譯程序完成編譯鏈接等操作,一般是不會出錯。注意,make本身不是編譯工具。

# make install

make install一般就更不會出錯了。

成功的話,軟件相關的所有文件就都在--prefix選項所指定的/usr/local/memcached目錄中了。以下是簡單的測試啟動。

[root@C7 memcached]# /usr/local/memcached/bin/memcached -d -u zwl
[root@C7 memcached]# ps aux | grep -i "memcached"
zwl        8965  0.0  0.3 413848  3124 ?        Ssl  17:02   0:00 /usr/local/memcached/bin/memcached -d -u zwl
root       8978  0.0  0.0 112708   992 pts/0    S+   17:02   0:00 grep --color=auto -i memcached
[root@C7 memcached]# netstat -ntulp | grep -i "memcached"
tcp        0      0 0.0.0.0:11211           0.0.0.0:*               LISTEN      8965/memcached      
tcp6       0      0 :::11211                :::*                    LISTEN      8965/memcached

make還有很多其他的子命令,可深入了解。

# make test|check
# make installcheck
# make clean
# make distclean
# make uninstall

安裝后還沒完,源碼安裝后,由於安裝位置是自定義的,所以系統在默認的PATH環境變量下、庫路徑下、man手冊路徑下,可能無法找到我們所安裝的軟件。因此涉及到以下操作。

1、導出二進制程序目錄至PATH環境變量中;

  編輯文件/etc/profile.d/NAME.sh

  export PATH=/PATH/TO/BIN:$PATH

2、導出庫文件路徑

  編輯/etc/ld.so.conf.d/NAME.conf

  /usr/local/apache2/lib

  讓系統重新生成緩存:

  ldconfig [-v]

3、導出頭文件

  基於字符鏈接方式實現:

  # ln -sv ...

4、導出幫助手冊

  編輯/etc/man.config文件

  添加一個MANPATH

5、在源碼目錄的scripts目錄下,有管理腳本,需要復制到相關的目錄。

  scripts/memcached.service

 

總結

限於篇幅有限、本人懶惰以及專業不足,因此本博文可能寫的不是很完善,特別是在源碼編譯安裝這塊。好在基本的軟件安裝都可通過yum來實現,該安裝方式所安裝的軟件特性也基本符合大眾需求。

還有一種可實現類似於源碼編譯安裝的叫做Source RPM安裝,大家可參考一下。

Working with Source RPMs


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM