Linux中程式包管理及程式的原始碼編譯

Dabric發表於2019-04-16

程式的原始碼編譯

在此之前我們思考一個問題,利用高階語言編(比如c/c++)寫出來的程式是如何讓計算機識別並執行的呢?我們都知道計算機只能識別二進位制格式的檔案,對於高階語言編寫出來的程式,我們必須要有一個“翻譯官”作為與計算機交流的一箇中間者,將高階語言編寫出來的程式“翻譯”成二進位制檔案以便讓計算機識別,這個“翻譯官”稱之為編譯器,把“翻譯”的過程稱為編譯。

一個程式的開發,可能要編寫好多的功能,幾個程式之間有時候某些需求功能是相同的,這時在編寫程式的時候不可能每次都將這些功能都實現,這樣只會增加程式碼的冗餘量,而且在計算機當中我們有一句話,別人已經實現的東西,我們拿來直接用就行,沒有必要事事都親歷為之。所以就出了一些庫函式開發的人員,他們將一些通用的功能編寫成為函式庫並編譯成二進位制檔案,為上層程式提供呼叫的介面,在編寫程式的時候有些功能的實現就可以通過呼叫函式庫來實現。總而言之,函式庫是將底層的一些功能實現之後為上層程式提供呼叫介面的特殊程式。

c原始碼編譯過程

Linux上最標準的程式語言為C,利用C語言程式程式原始碼的編寫,然後利用編譯程式gcc來編譯,就可以實現一個可以執行的binary program,其流程如下:
原程式編譯流程示意圖

事實上,使用類似gcc這樣的編譯程式進行編譯的過程並不簡單,對於大型的軟體程式而言,並不會僅有一支程式組成,而是有一堆程式程式碼檔案,這些檔案之間存在著某種聯絡,可能一個子程式的執行要依賴另一個子程式的執行,也就是存在程式如何編譯的問題,即編譯指令,make命令的相關功能可以簡化編譯過程的指令。

通常每個源程式都會提供configure程式,該程式是用來偵測使用者的工作環境,以及該工作環境中是否有該軟體所需的其它功能,通過該程式我們可以定製軟體的功能,定義其檔案安裝的路徑,庫檔案的路徑。當這個程式執行完成後,會生成一個Makefile檔案,裡面記錄了原始碼如何編譯的詳細資訊,make命令通過Makefile檔案描述源程式之間的相互關係並自動維護編譯工作。

configure偵測程式所偵測的內容:

  • 是否有合適的編譯程式可以編譯軟體的程式程式碼;
  • 是否已經存在本軟體所需的庫函式,或其他依賴的軟體;
  • 作業系統平臺是否適合本軟體,包括Linux的核心版本;
  • 標頭檔案和驅動程式是否存在;

make、configure工作流程
make,configure工作流程

Linux中c原始碼編譯安裝

第一步:開源程式原始碼的獲取
第二步:開發工具及開發環境的準備
  • 開發工具:make, gcc等

  • 開發環境:開發庫,標頭檔案,glibc

    通過“包組”提供開發元件:CentOS6上通過安裝以下兩個開發元件:
    Development Tools
    Server Platform Development

第三步:執行congfigure程式

該程式通過選項傳遞引數,指定啟用特性、安裝路徑等。執行時會參考使用者的指定以及Makefile.in檔案生成makefile。最後檢查依賴的外部環境。

常用選項

Options Function
- -help 獲取其支援使用的選項
- -prefix=/PATH/TO/SOMEWHERE 指定預設安裝位置,預設為/usr.local/
- -sysconfdir=/PATH/TO/SOMEWHERE 配置檔案安裝位置
第四步:make
  • 根據makefile檔案,構建應用程式
第五步:make install
安裝後的配置

當我們指定程式的安裝路徑以及配置檔案的安裝路徑後,我們要新增程式的環境變數、庫檔案的路徑、程式的幫助手冊:

  1. 匯出二進位制程式目錄至PATH環境變數中:
    編輯檔案/etc/profile.d/NAME.sh
	export PATH=/PATH/TO/BIN:$PATH
  1. 匯出庫檔案路徑
    a. 編輯/etc/ld.so.conf.d/NAME.conf
    新增新的庫檔案所在目錄至此檔案中
    b. 讓系統重新生成快取
	# ldconfig [-v]
  1. 匯出標頭檔案
    標頭檔案一般在系統上/usr/include目錄下,基於連結的方式實現(將程式安裝目錄下的include檔案連結至/usr/include)
	# ln -sv
  1. 匯出幫助手冊
    編輯/etc/man.config檔案,新增安裝的程式的man手冊路徑

rpm程式包管理器

rpm(RedHat Package Manager),是以一種資料庫記錄的方式來將你所需的軟體安裝到Linux系統上的一套管理機制。RPM將軟體先編譯,然後打包成RPM機制的包檔案,通過包裝好的軟體裡預設的資料庫記錄,記錄了該軟體安裝時所依賴的其他屬性軟體。其功能是將編譯好的應用程式的各組成檔案打包成一個或幾個程式包檔案,從而更方便地實現程式包的安裝、升級、解除安裝、查詢校驗等管理操作。不同的Linux發行版本的軟體管理機制不同:

Distribution代表 軟體管理體制 使用命令 線上升級機制(指令)
Red Hat/Fedora RPM rpm, rpmbuild YUM(yum)/dnf
Debian/Ubuntu DPKG dpkg APT(apt-get)

rpm包命名格式

rpm包是在原始碼的基礎上進行命名的。那麼原始碼的命名格式是什麼呢?
原始碼:name-VERSION.tar.gz
VERSION包含major.minor.release
major:主版本號,代表一個重大的版本分支
minor: 次版本號,在重大分支中進行一小部分的功能修改
release: 發行號,修整了BUG,或者對一小部分的程式碼進行修改
rpm包命名格式:
name-VERSION-release.arch.rpm
VERSION: major.minor.release
release.arch: rpm包的發行號
其中arch表示該rpm包適用的系統框架,一般rpm包命名格式的系統框架平臺有:

Archetecture Introduction
i386 32位的系統,pentum、Inter Core 2與K8系列的CUP,其中i指的是Inter相容的CUP
i586 針對586等級的計算機框架平臺,包括pentum第一代MMX CPU名,AMD的K5,K6系列CPU
i686 在pentum II以後的Intel系列CUP,以及K7以後等級的CUP
x86_64 64位的系統,包括Intel的Core 2以上等級CPU,以及AMD的Athlon64以後的CUP
noarch 沒有系統框架的限制,適用於任何系統框架,這類的RPM包裡面應該沒有binary program存在,較常出現的就是屬於shell script相關的軟體

RPM包一般分為主包和支包,主包一般是個軟體的基本功能的軟體包,而支包是整個軟體中實現其他一些功能的包,這種方式我們稱其為拆包。主包和支包的格式如下:

主包:name-VERSION-release.arch.rpm
支包:name-function-VERSION-release.arch.rpm
	其中function一般有:devel, utils, libs...

rpm包內包含的內容

對於一個rpm包中每個程式包中都單獨包含以下內容:

  • 檔案清單
  • 安裝或解除安裝時執行的指令碼

RPM包是通過資料庫實現一些列的管理功能,該資料庫所在的路徑為/var/lib/rpm/目錄下,其資料庫中包含了以下內容:

  • 程式包的名稱和版本
  • 程式包的依賴關係
  • 程式的功能說明
  • 安裝生成的各檔案的檔案路徑及校驗碼資訊
  • 等等等

rpm包獲取的途徑

  1. 系統大型版的光碟或官方的檔案伺服器(或映象站點)
	http://mirrors.aliyun.com
	http://mirrors.163.com
	http://mirrors.sohu.com
  1. 專案的官方站點
  2. 第三方組織
 	EPEL
	搜尋引擎
		http://pkgs.org
		http://rpmfind.net
		htto://rpm.pbone.net
  1. 自己動手製作rpm包

rpm命令管理程式包

前面說過rpm程式包管理器主要實現程式的安裝、升級、解除安裝、查詢和校驗以及資料庫維護。

rpm命令格式

rpm [OPTIONS] [PACKAGE_FILE]

常用選項

Options Function
-i, - -install 安裝軟體包
-u, - -update, -F, --freshen 升級軟體包
-e, - -erase 解除安裝程式
-q, - -query 查詢軟體相關的資訊
-V, - -verify 校驗
- - builddb, - -initdb 資料庫維護
-v verbose,詳細資訊
-vv 更詳細的輸出
安裝

安裝命名格式

# rpm {-i | --install} [install-options] PACKAGE_FILE...
# rpm -ivh PACKAGE_FILE...     安裝軟體包常用的命令

[install-option]

Options Function
-h, - -hash hash marks輸出進度條,每個#號表示2%的進度
- -test 測試安裝,檢查並報告依賴關係及衝突訊息等
- -nodeps 忽略依賴關係,不建議
- -replacepkgs 重新安裝
- -noscripts rpm包可以自帶指令碼,該安裝選項是禁止指令碼的執行。rpm包自帶的指令碼分為四類,以下一一詳述
%per, - -nopre preinstall安裝過程開始之前執行的指令碼
%post, - -nopost postinstall安裝過程完成之後執行的指令碼
%preun, - -nopreun preuninstall解除安裝過程真正開始執行之前執行的指令碼
%psotun, - -nopostun postuninstall解除安裝過程完成之後執行的指令碼
- -nosignature 不檢查包簽名資訊,不檢查來源合法性
- - nodigest 不檢查包完整性資訊
升級

升級命名格式

# rpm {-U|--upgrade} [install-options] PACKAGE_FILE ...    對以安裝的軟體進行升級,如果該軟體沒有安裝,則安裝之
# rpm {-F|--freshen} [install-options] PACKAGE_FILE ...    只能對以安裝的軟體進行升級

常用的升級命令:
# rpm -Uvh PACKAGE_FILE...
# rpm -Fvh PACKAGE_FILE...

[install-options]

Options Function
- -oldpackage 降級
- -force 強制升級

注意:

  1. 不要對核心進行升級操作,Linux支援多核心版本並存,因此直接安裝新版本核心
  2. 如果某原程式包的配置檔案安裝後曾被修改過,升級時,新版本的程式提供的同一配置檔案不會覆蓋原有版本的配置檔案,而是把新版本的配置檔案重新命名(FILENAME.rpmnew)後提供
解除安裝

軟體解除安裝的命令格式

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

常用選項

Options Function
- -allmatches 解除安裝所有匹配指定名稱的程式包的各版本
- -nodeps 忽略依賴關係
- -test 測試解除安裝,dry run模式
查詢

rpm包的查詢的所有操作都是基於/var/lib/rpm/目錄下的資料庫進行的。

軟體查詢的命令格式

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

[select-options]

Select-options Function
PACKAGE_NAME 查詢指定的程式包是否已經安裝,及其版本
-a, - -all 查詢所有已經安裝過的包
-f FILE 查詢指定的檔案由哪個程式包安裝生成
-p, - -package PACKAGE_NAME 用於實現對未安裝的程式包執行查詢操作
- -whatprovides CAPABILITY 查詢指定的CAPABILITY由哪個程式包提供
- -whatrequires CAPABILITY 查詢指定的CAPABILITY被哪個包所依賴

[query-options]

Query-options Function
- -changelog 查詢rpm包的chagelog
-l, - -list 程式安裝生成的所有檔案列表
-i, - -info 程式包相關的資訊、版本號、大小、所屬的包組等
-c, - -configures 查詢指定的程式包提供的配置檔案
-d, - -docfiles 列出指定的程式包提供的文件
- -provides 列出指定的程式包提供的所有的CAPABILITY
-R, - -require 查詢指定的程式包的依賴關係
- -scripts 查詢程式包自帶的指令碼片段
校驗

軟體校驗的命令格式

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

在軟體包校驗之前到獲取並匯入信任的包製作者的金鑰。對於CentOS發行版來說匯入金鑰的密令如下:

# rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7

驗證軟體包時,安裝此組織簽名的程式時,會自動執行驗證,或者手動驗證,命令如下:

# rpm -K PACKAGE_FILE
資料庫重建

rpm管理器資料庫重建路徑:/var/lib/rpm/,通過man命令可以獲取rpm資料庫的資訊:
CentOS 6: man rpm
CentOS 7: man rpmdb

資料庫重建的命令格式

# rpm {--initdb|--rebuilddb} [-v] [--dbpath DIRECTORY] [--root DIRECTORY]

常用選項

Options Function
- -initdb 初始化資料庫,當前無任何資料庫可初始化建立一個新的,當前有時不執行任何操作
- -rebuilddb 重新構建,通過讀取當前系統上所有已經安裝過的程式包進行重新建立

總結

由於RPM是利用預先編譯並打包成為RPM檔案格式後,再進行安裝的一種方式,並且還能夠進行資料庫的記載,所有RPM有以下的優點:

  • RPM內含已經編譯過的程式與配置檔案等資料,可以讓使用者免除重新編譯的困擾
  • RPM在被安裝之前,會先檢查系統的相關資訊,比如作業系統的版本,可以避免檔案被錯誤安裝
  • RPM檔案本身提供軟體版本資訊、相依賴軟體的名稱、軟體用途說明、軟體所含檔案等資訊,便於瞭解軟體
  • RPM管理的方式視同資料庫記錄RPM檔案的相關引數,便於升級、解除安裝、查詢與驗證

yum使用詳解

上面提到過rpm包的拆包的現象,其是為了重複利用既有的軟體的功能,很多軟體都會以函式庫的方式被抽出部分功能,以便其他軟體的呼叫。因為這個現象,RPM包管理器就會出現所謂的軟體包依賴的問題產生,所以YUM(yellow dog, Yellow Update Modifier)線上升級工具來解決軟體包的依賴關係。

yum的工作原理

yum是基於C/S架構實現的,YUM伺服器端儲存了眾多RPM包,通過分析這些軟體的依賴關係,將軟體內的記錄資訊記錄下來。然後將這些資訊分析後記錄成軟體相關性的列表,即就是包的相關的後設資料檔案(放置於特定的目錄下:repodata)這些列表資料與軟體所在的本機或網路位置稱為軟體倉庫。當客戶端有軟體安裝的需求時,客戶端主機會主動向yum伺服器的軟體庫網址下載清單列表,然後通過列表的資料與本機RPM資料庫已存在的軟體資料相比較,就能安裝所需的具有相依賴的軟體。整個流程如下圖所示:
YUM工作原理圖

yum客戶端的配置

yum伺服器為Linux客戶端提供軟體倉庫,那麼Linux客戶端怎麼連線軟體倉庫?通過對本地和yum相關的配置檔案進行配置就可以連線yum伺服器了。yum客戶端的配置檔案:

/etc/yum.conf: 為所有倉庫提供公共配置
/etc/yum.repos.d/*.repo: 為倉庫的指向提供配置

倉庫指向的定義:
[repositoryID]
name=Some name for this repository
baseurl=url://path/to/repository/
enable={1|0}
gpgcheck={1|0}
gpgkey=UR:
enablegroup={1|0}
failovermethod={roundrobin|priority}
	預設為:roundrobin,意為隨機挑選;
cost=
	預設為1000

yum的repo配置檔案中可用的變數:
	$releasever: 當前OS的發行版的主版本號;
	$arch: 平臺;
	$basearch: 基礎平臺;
	$YUM0-$YUM9

yum命令的用法

yum命令的格式

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

常用選項

Options Function
- -nogpgcheck 禁止進行gpg check
-y 自動回答為“yes”
-q 靜默模式
- -disablerepo=repoidglob 臨時禁用此處指定的repo
- -enbalerepo=repoidglob 臨時啟用此處指定的repo
- -noplugins 禁用所有外掛

command

  • 顯示倉庫列表
repolist [all|enabled|disabled]
  • 顯示程式包
list
	# yum list [all | glob_exp1] [glob_exp2] [...]
	# yum list {available|installed|updates} [glob_exp1] [...]
  • 安裝程式包
install package1 [package2] [...]
reinstall package1 [package2] [...] (重新安裝)
  • 升級程式包
update [package1] [package2] [...]
downgrade package1 [package2] [...] (降級)
  • 檢查可用升級
check-update
  • 解除安裝程式包
remove | erase package1 [package2] [...]
  • 檢視程式包information
info [...]
  • 檢視指定的特性(可以是某檔案)是由哪個程式包所提供
provides | whatprovides feature1 [feature2] [...]
  • 清理本地快取
clean [ packages | metadata | expire-cache | rpmdb | plugins | all ]
  • 構建快取
makecache
  • 搜尋
search string1 [string2] [...]
以指定的關鍵字搜尋程式包名及summary資訊
  • 檢視指定包所依賴的CAPABILITY
deplist package1 [package2] [...]
  • 檢視yum事務歷史
history [info|list|packages-list|packages-info|summary|addon-info|redo|undo|rollback|new|sync|stats]
  • 安裝升級本地程式包
下載到本地的rpm包也可以用yum進行安裝,可以解決安裝過程中的依賴關係。
localinstall rpmfile1 [rpmfile2] [...]
	 (maintained for legacy reasons only - use install)
localupdate rpmfile1 [rpmfile2] [...]
	 (maintained for legacy reasons only - use update)

  • 包組管理的相關命令
* groupinstall group1 [group2] [...]
* groupupdate group1 [group2] [...]
* grouplist [hidden] [groupwildcard] [...]
* groupremove group1 [group2] [...]
* groupinfo group1 [...]

建立yum倉庫

createrepo [options] <directory>

相關文章