鳥哥的 Linux ADSL 私房菜


原始碼與 Tarball 套件管理員
最近更新日期:2004/04/10
 
我們在 Linux 是什麼 一文當中,提到了 GNU 與 GPL 還有開放源碼等咚咚,不過,前面都還沒有提到真正的開放源碼是什麼的訊息!在這一章當中,我們將藉由 Linux 作業系統裡面的執行檔,來理解什麼是可執行的程式,以及瞭解什麼是編譯器。另外,與程式息息相關的函式庫(library)的資訊也需要瞭解一番!不過, 在這個章節當中,鳥哥並不是要您成為一個開放源碼的程式設計師,而是希望您可以瞭解如何將開放源碼的程式設計、加入函式庫的原理、透過編譯而成為可以執行 的 binary file ,最後該執行檔可被我們所使用的一連串過程!
 
瞭解上面的咚咚有什麼好處呢?!因為在 Linux 的世界裡面,我們常常需要自行安裝套件在自己的 Linux 系統上面,所以如果您有簡單的程式編譯概念,那麼將很容易進行套件的安裝,甚至在發生套件編譯過程中的錯誤時,您也可以自行作一些簡易的修訂呢!而最傳統的套件安裝過程,自然就是由原始碼編譯而來的囉!所以,在這裡我們將介紹最原始的套件管理方式:使用 Tarball 來安裝與升級管理我們的套件喔!
 
前言
  :什麼是開放源碼、編譯器與可執行檔
  :什麼是函式庫
  :什麼是  make 與 configure
  :什麼是 Tarball 的套件
  :如何安裝與升級套件
一個簡單的範例
  :印出 Hello World
  :副程式的編譯
  :加入連結的函式庫
  :gcc 的用法
make 的簡易用法
  :為什麼要用 make
  :make 的基本語法與變數
Tarball 的管理與建議
  :使用原始碼管理套件所需要的基礎套件
  :Tarball 安裝的基本步驟
  :一般 Tarball 套件安裝的建議事項( 如何移除?升級? )
  :一個簡單的範例、利用 ntp 來示範
  :利用 patch 更新原始碼
函式庫管理
  :動態與靜態函式庫
  :ldconfig 與 /etc/ld.so.conf, ldd,
檢驗套件軟體的正確性
  :md5sum
重點回顧
參考資源
課後練習

前言:
如果鳥哥想要在我的 Linux 伺服器上面跑網頁伺服器( WWW server )這項服務,那麼我應該要做些什麼事呢?呵呵!當然就一定需要『安裝網頁伺服器的套件』囉! 如果鳥哥的伺服器上面沒有這個套件的話,那當然也就無法啟用 WWW 的服務啦!所以啦,想要在您的 Linux 上面進行一些有的沒的功能,學會『如何安裝套件』是很重要的一個課題!
 
咦!安裝套件有什麼難的?在 Windows 作業系統上面安裝套件時,不是只要一直給他按『下一步』就可以安裝妥當了嗎?話是這樣說沒錯啦,不過,也由於如此,所以在 Windows 系統上面的軟體都是一模一樣的,也就是說,您『無法修改該軟體的原始程式碼』,因此,萬一您想要增加或者減少該軟體的某些功能時,呵呵!大概只能求助於當初發行該軟體套件的廠商了!
 
或許你會說:『唉呦!我不過是一般人,不會用到多餘的功能,所以不太可能會更動到程式碼的部分吧!?』如果您這麼想的話,很抱歉∼是有問題的!怎麼說呢? 像目前網路上面的病毒、黑客軟體、臭蟲程式等等,都可能對您的主機上面的某些軟體造成影響(這是因為軟體開發者在寫作之初可能並沒有想到某些問題所致!),導致主機的當機或者是其他資料損毀等等的傷害。如果您可以藉由安全資訊單位所提供的修訂方式進行修改,那麼您將可以很快速的自行修補好該軟體的漏洞,而不必一定要等到套件開發商提供修補的程式包哩!要知道,提早補洞是很重要的一件事。
 
這樣說可以瞭解 Linux 的優點了嗎?!沒錯!因為 Linux 上面的套件幾乎都是經過 GPL 的授權,所以每個套件幾乎均提供原始程式碼,並且您可以自行修改該程式碼,以符合您個人的需求呢!很棒吧!這就是開放源碼( Open source )的優點囉!不過,到底什麼是開放源碼?這些程式碼是什麼咚咚?又 Linux 上面可以執行的相關套件檔案與開放源碼之間是如何轉換的?不同版本的 Linux 之間能不能使用同一個執行檔?或者是該執行檔需要由原始程式碼的部分重新進行轉換?這些都是需要釐清觀念的。底下我們先就原始程式碼與可執行檔來進行說明。
 

什麼是開放源碼、編譯器與可執行檔?
在討論程式碼是什麼之前,我們先來談論一下什麼是可執行檔?我們說過,在 Linux 系統上面,一個檔案能不能被執行看的是有沒有可執行的那個權限( 具有 x permission ),不過,Linux 系統上真正認識的可執行檔其實是二進位檔案( binary file ),例如 /usr/bin/passwd, /bin/touch 這些個檔案即為 binary 的可執行檔案!或許您會說,咦! shell scripts 不是也可以執行嗎?!其實 shell scripts 只是利用 shell ( 例如 bash )這支程式的功能進行一些判斷式,而最終執行的除了 bash 提供的功能外,仍是呼叫一些已經編譯好的 binary 檔案來執行的呢!( bash 本身就是 binary file 喔!)那麼我怎麼知道一個檔案是否為 binary 呢?!還記得我們在 Linux 檔案與目錄管理 裡面提到的 file 這個指令的功能嗎?!對啦!用他就是了!我們現在來測試一下:
 
# 先以系統的檔案測試看看:
[root@test root]# file /bin/bash
/bin/bash: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, dynamically linked (uses shared libs), stripped
 
# 如果是我們在 shell script 那個章節寫的 test01-hello.sh 檔案呢?
[root@test root]# file test01-hello.sh
test01-hello.sh: Bourne-Again shell script text executable
  
看到了吧!如果是 binary file 而且是可以執行的時候,他就會顯示執行檔類別 ( ELF 32-bit LSB executable ),同時會說明是否使用動態函式庫( shared libs ),而如果是一般的 script ,那他就會顯示出 text executables 之類的字樣!(事實上,test01-hello.sh 的資料顯示出 Bourne-Again ... 那一行,是因為您的 scripts 上面第一行有宣告 #!/bin/bash 的緣故,如果您將 script 的第一行拿掉,呵呵!那麼不管 test01-hello.sh 的權限為何,他其實顯示的是 ASCII 文字檔的資訊喔!)
 
既然 Linux 作業系統真正認識的其實是 binary file ,那麼我們是如何做出這樣的一支 binary  的程式呢?!首先,我們必須要寫程式,用什麼東西寫程式?就是一般的文書處理器啊!我都喜歡使用 vi 來進行程式的撰寫,寫完的程式就是所謂的原始程式碼囉!這個程式碼檔案其實就是一般的純文字檔( text file )。在完成這個原始碼檔案的編寫之後,再來就是要將這個檔案『編譯』成為作業系統看的懂得 binary file 囉!而要編譯自然就需要『編譯器』來動作,經過編譯器的編譯之後,就會產生一支可以執行的 binary file 囉。
 
舉個例子來說,在 Linux 上面最標準的程式語言為 C ,所以我使用 C 的語法進行原始程式碼的書寫,寫完之後,以 Linux 上標準的 C 語言編譯器 gcc 這支程式來編譯,就可以製作一支可以執行的 binary file 囉。整個的流程有點像這樣:
 

圖一、簡易的 gcc 編譯流程
 
事實上,在編譯的過程當中,還會產生所謂的目標檔( Object file ),這些檔案是以 *.o 的附檔名樣式存在的!至於 C 語言的原始碼檔案通常以 *.c 作為附檔名。此外,有的時候,我們會在程式當中『引用、呼叫』其他的外部副程式,或者是利用其他套件提供的『函數功能』,這個時候,我們就必須要在編譯的過程當中,將該函式庫給他加進去,如此一來,編譯器就可以將所有的程式碼與函式庫作一個連結( Link )以產生正確的執行檔囉。

什麼是函式庫?
在前一小節的圖一示意圖中,在編譯的過程裡面有提到函式庫。好啦,那麼什麼是函式庫呢?先舉個例子來說,我們的 Linux 系統上,系統通常已經提供一個可以進行身份驗證的模組,稱為 PAM ,這個 PAM 提供的功能可以讓很多的程式在被執行的時候,除了可以驗證使用者登入的資訊外,還可以將身份確認的資料記錄在登錄檔( log file, 請查閱後續的 認識登錄檔 一文 )裡面,以方便系統管理員的追蹤!既然有這麼好用的功能,那如果我要編寫具有身份認證功能的程式時,直接引用該 PAM 的功能就好啦,如此一來,我就不需要重新設計認證機制囉!也就是說,只要在我寫的程式碼裡面,設定去呼叫 PAM 的函式功能,呵呵!我的程式就可以利用 Linux 原本就有的身份認證的程序咯!除此之外,其實我們的 Linux 核心( kernel )也提供了相當多的函式庫來給硬體開發者利用喔。
 
函式庫又分為動態與靜態函式庫,這兩個咚咚的分別我們在後面的章節再加以說明。這裡我們以一個簡單的流程圖,來示意一支有呼叫外部函式庫的程式的執行情況。
 

圖二、程式引用函式庫的示意圖
 
很簡單的示意圖啊!^_^!而如果要在程式裡面加入引用的函式庫,就需要如圖一所示,亦即在編譯的過程當中,就需要加入函式庫的相關設定囉。
 
事實上, Linux 的核心提供很多的核心相關函式庫與外部參數,這些核心功能在設計硬體的驅動程式的時候是相當有用的資訊,這些核心相關資訊大多放置在 /usr/include, /lib, /usr/lib 裡面哩!我們在本章的後續小節再來探討。

什麼是  make 與 configure ?
事實上,使用類似 gcc 的編譯器來進行編譯的過程並不簡單,因為除了每個主程式與副程式均需要寫上一筆編譯過程的指令外,還需要寫上最終的連結程序。程式碼小的時候還好,如果是類似 WWW 伺服器軟體( 例如 Apache ),或者是類似核心的原始碼,動則數百 MBytes 的資料量,呵呵!指令會寫到瘋掉∼這個時候,我們就可以使用 make 這個指令的相關功能來進行編譯過程的指令簡化了!
 
當執行 make 時,make 會在當時的目錄下搜尋 Makefile ( or makefile ) 這個文字檔,而 Makefile 裡面則記錄了原始碼如何編譯的詳細資訊!  make 會自動的判別原始碼是否經過變動了,而自動更新執行檔,是軟體工程師相當好用的一個輔助工具呢!
 
咦!make 是一支程式,會去找 Makefile ,那 Makefile 怎麼寫?呵呵!通常軟體開發商都會寫一支偵測程式來偵測使用者的作業環境,以及該作業環境是否有軟體開發商所需要的其他功能,該偵測程式偵測完畢後,就會主動的建立這個 Makefile 的規則檔案啦!通常這支偵測程式的檔名為 configure 或者是 config 。
 
咦!?那為什麼要偵測作業環境呢?!在 什麼是 Linux 那個章節當中,不是有提到 Linux 不過就是一個核心嗎?!是這樣沒錯啦!但是您必須要瞭解的是,某些軟體套件需要一些相關的套件輔助,並且,某些驅動程式則是適用在不同的核心系統( 因為核心提供的函式庫可能並不相同,例如 kernel 2.2.xx 與 kernel 2.4.xx 就不太一樣! ),並且每個 Linux distribution 所提供的函式庫名稱與路徑可能也不太一樣,所以說,在 Mandrake 上面可以執行的一個 binary file ,直接複製到 Red Hat 平台上,可不見得可以順利執行( 事實上,是不太可能可以執行啦! ^_^ )。所以啦,原始碼寫出來之後,需要針對不同的作業環境來進行編譯的行為吶!這個時候就很需要 configure 以及 make 的功能啊!
 
詳細的 make 用法與 Makefile 規則,在後續的小節裡面再探討囉!

什麼是 Tarball 的套件 ?
從前面幾個小節的說明來看,我們知道所謂的原始程式碼,其實就是一些寫滿了程式碼的純文字檔案。那我們從前面的 檔案的壓縮與打包 章節當中,也瞭解了純文字檔其實是很浪費硬碟空間的一種檔案格式!( 想一想,一個核心的原始碼檔案大約要 200~300 MB 以上,如果每個人都去下載這樣的一個核心檔案,呵呵!那麼網路頻寬不被吃的死翹翹才怪呢! )所以啦,如果能夠將這些原始碼透過檔案的打包與壓縮技術來將檔案的數量與容量減小,不但讓使用者容易下載,套件開發商的網站頻寬也能夠節省很多很多啊!這就是 Tarball 檔案的由來囉!
 
所謂的 Tarball 檔案,其實就是將套件的所有原始碼檔案先以 tar 打包,然後再以壓縮技術來壓縮,通常最常見的就是以 gzip 來壓縮了。因為利用了 tar 與 gzip 的功能,所以 tarball 檔案一般的附檔名就會寫成 *.tar.gz 或者是簡寫為  *.tgz 囉!也就是說, Tarball 套件解壓縮之後,裡面的檔案通常就會有: 其中最重要的是那個 INSTALL 或者是 README 這兩個檔案,通常您只要能夠參考這兩個檔案,呵呵! Tarball 套件的安裝是很簡單的啦!我們在後面的章節會再繼續介紹 Tarball 這個玩意兒。

如何安裝與升級套件
將原始碼作了一個簡單的介紹,也知道了系統其實認識的可執行檔是 binary file 之後,好了,得要聊一聊,那麼怎麼安裝與升級一個 Tarball 的套件?為什麼要安裝一個新的套件呢?當然是因為我們的主機上面沒有該套件囉!那麼,為何要升級呢?!原因可能有底下這些: 在上面的需求當中,尤其需要注意的是第二點,當一個套件有安全上的顧慮時,千萬不要懷疑,趕緊更新套件吧!否則造成網路危機,那可不是鬧著玩的!那麼更新的方法有哪些呢?基本上更新的方法可以分為兩大類,分別是: 上面第一點很簡單,就是直接以 Tarball 在自己的機器上面進行偵測、編譯、安裝與設定等等動作來升級就是了。不過,這樣的動作雖然讓使用者在安裝過程當中具有很高的彈性,但畢竟是比較麻煩一點,如果 Linux distribution 廠商能夠針對自己的作業平台先進行編譯等過程,再將編譯好的 binary file 釋出的話,那由於我的系統與該 Linux distribution 的環境是相同的,所以他所釋出的 binary file 就可以在我的機器上面直接安裝啦!省略了偵測與編譯等等繁雜的過程呢!目前很多 binary file 升級的機制呢,包括有 Red Hat 發展的 RPM 與 up2date, yum 等線上更新模式;Mandrake 的 urpmi 套件更新方式; Debian 使用的 dpkg ; Sun Unix 使用的 pkg ,以及目前很流行的 apt 線上更新模式等等,以使用率來說,目前最傳統的是以 Tarball 直接進行編譯的安裝與升級,而另一個則是以 RPM 相關的機制來進行安裝與升級囉!本章節主要針對 Tarball ,至於 RPM 則留待下個章節再來介紹呢!
 
好了,那麼一個套件的 Tarball 是如何安裝的呢?基本流程是這樣的啦:
  1. 將 Tarball 由廠商的網頁下載下來;
  2. 將 Tarball 解開,產生很多的原始碼檔案;
  3. 開始以 gcc 進行原始碼的編譯( 會產生目標檔 object files );
  4. 然後以 gcc 進行函式庫、主、副程式的連結,以形成主要的 binary file;
  5. 將上述的 binary file 以及相關的設定檔安裝至自己的主機上面。
上面第 3, 4 步驟當中,我們可以透過 make 這個指令的功能來簡化他,所以整個步驟其實是很簡單的啦!只不過您就得需要至少有 gcc 以及 make 這兩個套件在您的 Linux 系統裡面才行喔!詳細的過程以及需要的套件我們在後面的章節繼續來介紹的啦!

一個簡單的範例:
剛剛我們都僅只是在螢幕上面印出一些字眼而已,如果說要計算數學公式呢?!例如我們想要計算出三角函數裡面的 sin(90度角),要注意的是,大多數的程式語言都是使用徑度而不是一般我們在計算的『角度』, 180 度角約等於 3.14 徑度!嗯!那我們就來寫一下這個程式吧!
 
[guest@test guest]# vi sin.c
#include <stdio.h>
int main(void)
{
        float value;
        value = sin ( 3.14 / 2 );
        printf("%f\n",value);
}
# 上面這個檔案的內容可以在底下取得!
# http://linux.vbird.org/download/books/basic/source_code/sin.c
  
那要如何編譯這支程式呢?我們先直接編譯看看:
 
[guest@test guest]# gcc sin.c
/tmp/ccppUCx8.o(.text+0x1e): In function `main':
: undefined reference to `sin'
collect2: ld returned 1 exit status
  
特別注意上面的說明,唉啊!怎麼沒有編譯成功?它說的是『undefined reference to sin』,說的是『沒有 sin 的相關定義參考值!』,為什麼會這樣呢?這是因為 C 語言裡面的 sin 函示是寫在 libm.so 這個函式庫中,而我們並沒有在原始碼裡面加入相關的說明,所以當然就需要在編譯與連結的時候將這個函式庫給他連結進執行檔裡面啊!所以我們可以這樣做:
 
[guest@test guest]# gcc sin.c -lm -L/lib -L/usr/lib
# 特別注意,那個 -lm 可以拆開成兩部份來看,
# -l 是『加入某個函式庫(library)』的意思,而
# m 則是 libm.so 這個函式庫,其中, lib 與附檔名(.a 或 .so)不需要寫
# 所以 -lm 表示使用 libm.so (或 libm.a) 這個函式庫的意思∼
# 至於那個 -L 後面接的路徑呢?這表示:
#『我要的函式庫 libm.so 請到 /lib 或 /usr/lib 裡面搜尋!
[guest@test guest]# ./a.out
1.000000
  
上面的說明很清楚了吧!!不過,要注意的是,由於 Linux 預設是將函式庫放置在 /lib 與 /usr/lib 當中,所以您沒有寫 -L/lib 與  -L/usr/lib 也沒有關係的!不過,萬一哪天您使用的函式庫並非放置在這兩個目錄下,那麼 -L/path 就很重要了!否則會找不到函式庫喔!
 
除了連結的函式庫之外,您或許已經發現一個奇怪的地方,那就是在我們的 sin.c 當中第一行『 #include <stdio.h>』,這行說的是要將一些定義資料由 stdio.h 這個檔案讀入,這包括 printf 的相關設定。這個檔案其實是放置在 /usr/include/stdio.h 的!那麼萬一這個檔案並非放置在這裡呢?那麼我們就可以使用底下的方式來定義出要讀取的 include 檔案放置的目錄:
 
[guest@test guest]# gcc sin.c -lm -I/usr/include
  
-I/path 後面接的路徑( Path )就是設定要去搜尋相關的 include 檔案的目錄啦!不過,同樣的,預設值是放置在 /usr/include 底下,除非您的 include 檔案放置在其他路徑,否則也可以略過這個項目!
 
透過上面的幾個小範例,您應該對於 gcc 以及原始碼有一定程度的認識了,再接下來,我們來稍微整理一下 gcc 的簡易使用方法吧!

make 的簡易用法
在前言的部分我們提到過 make 的功能是可以簡化編譯過程裡面所下達的指令,同時還具有很多很方便的功能!那麼底下咱們就來試看看使用 make 簡化下達編譯指令的流程吧!
 

為什麼要用 make
先來想像一個案例,假設我的執行檔裡面包含了四個原始碼檔案,分別是 main.c haha.c sin_value.c cos_value.c 這四個檔案,這四個檔案您可以到 http://linux.vbird.org/download/books/basic/source_code/main.tgz 來下載,由於這四個檔案裡面包含了相關性,並且還用到數學函式在裡面,所以如果您想要讓這個程式可以跑,那麼就需要這樣編譯:
 
[guest@test guest]# gcc -c main.c
[guest@test guest]# gcc -c haha.c
[guest@test guest]# gcc -c sin_value.c
[guest@test guest]# gcc -c cos_value.c
# 先以上面的動作製作出四個目標檔,然後再進行下面的動作:
 
[guest@test guest]# gcc -o main main.o haha.o sin_value.o \
> cos_value.o -lm -L/usr/lib -L/lib
# 這樣就可以製作出  main 這個執行檔囉!執行看看吧!
 
[guest@test guest]# ./main
HaHa! I'm the King of the world
0.706825
0.707388
 
呵呵!要做好多動作啊!真是麻煩,如果可以的話,能不能一個步驟就給他完成上面所有的動作呢?試看看在這個目錄下建立一個 makefile 檔案,內容如下:
 
# 1. 先建立編譯的規則
[guest@test guest]# vi makefile
main: main.o haha.o sin_value.o cos_value.o
        gcc -o main main.o haha.o sin_value.o cos_value.o -lm
# 注意: gcc 之前是 <tab> 按鍵產生的空格喔!
 
# 2. 嘗試給他建立規則看看
[guest@test guest]# rm -f main *.o <==先將之前的目標檔去除
[guest@test guest]# make
cc    -c -o main.o main.c
cc    -c -o haha.o haha.c
cc    -c -o sin_value.o sin_value.c
cc    -c -o cos_value.o cos_value.c
gcc -o main main.o haha.o sin_value.o cos_value.o -lm
# 這個時候 make 會主動去讀取 makefile 這個檔案的內容,
# 並根據內容直接去給他編譯起相關的執行檔囉!
 
# 3. 如果再執行一次 make 會怎樣?!
[guest@test guest]# make
make: `main' is up to date.
# 看到了吧?!是否很方便呢?!
 
或許您會說:『如果我建立一個 shell script 來將上面的所有動作都集結在一起,不是具有同樣的效果嗎?』呵呵!效果當然不一樣,以上面的測試為例,我們僅寫出 main 需要的目標檔,結果 make 會主動的去判斷每個目標檔相關的原始碼檔案,並直接予以編譯,最後再直接進行連結的動作!哈哈!真的是很方便啊!此外,如果我們更動過某些原始碼檔案,則 make 也可以主動的判斷哪一個原始碼與相關的目標檔檔案有更新過,並僅更新該檔案,如此一來,將可大大的節省很多編譯的時間呢!要知道,某些程式在進行編譯的行為時,會消耗很多的 CPU 資源呢!所以說, make 有這些好處: 既然 make 有這麼多的優點,那麼我們當然就得好好的瞭解一下 make 這個令人關心的傢伙啦!而 make 裡面最需要注意的大概就是那個規則檔案,也就是 makefile 這個檔案的語法啦!底下我們針對 makefile 的語法來加以介紹囉。

make 的基本語法與變數
make 的語法可是相當的多而複雜的,有興趣的話可以到 http://www.gnu.org/software/make/manual/html_mono/make.html 去查閱相關的說明,鳥哥這裡僅列出一些基本的規則,重點在於讓讀者們未來在接觸原始碼時,不會太緊張啊! ^_^好了,基本的 makefile 規則是這樣的:
 
標的(target): 目標檔1 目標檔2
<tab>  gcc -o 欲建立的執行檔 目標檔1 目標檔2
 
那個標的(target)就是我們想要建立的資訊,而目標檔就是具有相關性的 object files ,那建立執行檔的語法就是以 <tab> 按鍵開頭的那一行!特別給他留意喔,『命令列必須要以 tab 按鍵作為開頭』才行!他的規則基本上是這樣的: 同樣的,我們以剛剛上一個小節的範例進一步說明,如果我想要有兩個以上的執行動作時,例如下達一個指令就直接清除掉所有的目標檔與執行檔,該如何製作呢?
 
# 1. 先建立編譯的規則
[guest@test guest]# vi makefile
main: main.o haha.o sin_value.o cos_value.o
        gcc -o main main.o haha.o sin_value.o cos_value.o -lm
clean:
        rm -f main main.o haha.o sin_value.o cos_value.o
 
# 2. 測試看看:
[guest@test guest]# make clean
rm -f main main.o haha.o sin_value.o cos_value.o
 
如此一來,我們的 makefile 裡面就具有至少兩個標的,分別是 main 與 clean ,如果我們想要建立 main 的話,輸入『make main』,如果想要清除有的沒的,輸入『make clean』即可啊!而如果想要先清除目標檔再編譯 main 這個程式的話,就可以這樣輸入:『make clean main』,如下所示:
 
[guest@test guest]# make clean main
rm -f main main.o haha.o sin_value.o cos_value.o
cc    -c -o main.o main.c
cc    -c -o haha.o haha.c
cc    -c -o sin_value.o sin_value.c
cc    -c -o cos_value.o cos_value.c
gcc -o main main.o haha.o sin_value.o cos_value.o -lm
 
這樣就很清楚了吧!?但是,您是否會覺得,咦! makefile 裡面怎麼重複的資料這麼多啊!呵呵!沒錯!所以我們可以再藉由 shell script 那時學到的『變數』來更簡化 makefile 喔:
 
[guest@test guest]# vi makefile
LIBS = -lm
OBJS = main.o haha.o sin_value.o cos_value.o
main: ${OBJS}
        gcc -o main ${OBJS} ${LIBS}
clean:
        rm -f main ${OBJS}
 
與 bash shell script 的語法有點不太相同,變數的基本語法為:
  1. 變數與變數內容以『=』隔開,同時兩邊可以具有空格;
  2. 變數左邊不可以有 <tab> ,例如上面範例的第一行 LIBS 左邊不可以是 <tab>;
  3. 變數與變數內容在『=』兩邊不能具有『:』;
  4. 在習慣上,變數最好是以『大寫字母』為主;
  5. 運用變數時,以 ${變數} 或 $(變數) 使用;
  6. 在該 shell 的環境變數是可以被套用的,例如提到的 CFLAGS 這個變數!
  7. 在指令列模式也可以給予變數。
由於 gcc 在進行編譯的行為時,會主動的去讀取 CFLAGS 這個環境變數,所以,您可以直接在 shell 定義出這個環境變數,也可以在 makefile 檔案裡面去定義,更可以在指令列當中給予這個咚咚呢!例如:
 
[guest@test guest]# make clean main "CFLAGS=-Wall"
# 這個動作在上 make 進行編譯時,會去取用 CFLAGS 的變數內容!
 
也可以這樣:
 
[guest@test guest]# vi makefile
LIBS = -lm
OBJS = main.o haha.o sin_value.o cos_value.o
CFLAGS = -Wall
main: ${OBJS}
        gcc -o main ${OBJS} ${LIBS}
clean:
        rm -f main ${OBJS}
 
咦!我可以利用指令列進行環境變數的輸入,也可以在檔案內直接指定環境變數,那萬一這個 CFLAGS 的內容在指令列與 makefile 裡面並不相同時,以那個方式輸入的為主?呵呵!環境變數取用的規則是這樣的:
  1. make 指令列後面加上的環境變數為優先;
  2. makefile 裡面指定的環境變數第二;
  3. shell 原本具有的環境變數第三。
此外,還有一些特殊的變數需要瞭解的喔: 所以我也可以將 makefile 改成:
 
[guest@test guest]# vi makefile
LIBS = -lm
OBJS = main.o haha.o sin_value.o cos_value.o
CFLAGS = -Wall
main: ${OBJS}
        gcc -o $@ ${OBJS} ${LIBS}  <==那個 $@ 就是 main !
clean:
        rm -f main ${OBJS}
 
這樣是否稍微瞭解了 makefile ( 也可能是 Makefile ) 的基本語法?這對於您未來自行修改原始碼的編譯規則時,是很有幫助的喔! ^_^!

Tarball 的管理與建議:
從原始碼的說明我們曉得要製作一個 binary 執行檔需要很多咚咚的呢!這包括底下這些基礎的套件: 雖然 Tarball 的安裝上面相當的簡單,如同我們前面幾個小節的例子,只要順著開發商提供的 README 與 INSTALL 檔案所載明的步驟來進行,安裝是很容易的。但是我們卻還是常常會在 BBS 或者是新聞群組當中發現這些留言:『我在執行某個程式的偵測檔案時,他都會告訴我沒有 gcc 這個套件,這是怎麼回事?』還有:『我沒有辦法使用 make 耶!這是什麼問題?』呵呵!這就是沒有安裝上面提到的那些基礎套件啦!
 
咦!為什麼使用者不安裝這些套件啊?呵呵!這是因為目前的 Linux distribution 大多已經偏向於桌上型電腦的使用,他們希望使用者能夠按照廠商自己的希望來安裝相關的套件即可,所以通常『預設』是沒有安裝 gcc 或者是 make 等套件的。所以啦,如果您希望未來可以自行安裝一些以 Tarball 方式釋出的套件時,記得請自行挑選想要安裝的套件名稱喔!例如在 Mandrake 或者是 Red Hat 當中記得選擇 Software Development 以及 Kernel Source Development 等相關字眼的群集呢。
 
那萬一我已經安裝好一部 Linux 主機,但是使用的是預設值所安裝的套件,所以沒有 make, gcc 等咚咚,該如何是好?呵呵!問題其實不大啦,目前使用最廣泛的 Mandrake 或者是 Red Hat 大多是以 RPM( 下一章會介紹 )來安裝套件的,所以,您只要拿出當初安裝 Linux 時的原版光碟,然後以下一章介紹的 RPM 來一個一個的加入到您的 Linux 主機裡面就好啦!很簡單的啦!

Tarball 安裝的基本步驟
我們提過以 Tarball 方式釋出的套件是需要重新編譯可執行的 binary file 的。而 Tarball 是以 tar 這個指令來打包與壓縮的檔案,所以啦,當然就需要先將 Tarball 解壓縮,然後到原始碼所在的目錄下進行 makefile 的建立,再以 make 來進行編譯與安裝的動作啊!所以整個安裝的基礎動作大多是這樣的:
  1. 將 tarball 檔案在 /usr/local/src 目錄下解壓縮;
  2. 進入新建立的目錄底下,去查閱 INSTALL 與 README 等相關檔案內容( 很重要的步驟! )
  3. 根據 INSTALL/README 的內容察看並安裝好一些相依的套件( 非必要 );
  4. 以自動偵測程式( configure 或 config )偵測作業環境,並建立 Makefile 這個檔案;
  5. 以 make 這個程式並使用該目錄下的 Makefile 做為他的參數設定檔,來進行 make ( 編譯或其他 )的動作;
  6. 以 make 這個程式,並以 Makefile 這個參數設定檔,依據 install 這個標的( target )的指定來安裝到正確的路徑!
注意到上面的第二個步驟,通常在每個軟體在釋出的時候,都會附上 INSTALL 或者是 README 這種檔名的說明檔,這些說明檔請『確實詳細的』閱讀過一遍,通常這些檔案會記錄這個軟體的安裝要求、軟體的工作項目、與軟體的安裝參數設定及技巧等,只要仔茠瑣\讀完這些檔案,基本上,要安裝好 tarball 的檔案,都不會有什麼大問題囉。至於 makefile 在製作出來之後,裡頭會有相當多的標的( target ),最常見的就是 install 與 clean 囉!通常『make clean』代表著將目標檔( object file )清除掉,『make』則是將原始碼進行編譯而已。注意喔!編譯完成的可執行檔與相關的設定檔還在原始碼所在的目錄當中喔!因此,最後要進行『make install』來將編譯完成的所有咚咚都給他安裝到正確的路徑去,這樣就可以使用該套件啦!
 
OK!我們底下約略提一下大部分的 tarball 軟體之安裝的指令下達方式:
  1. ./configure

  2. 這個步驟就是在建立 Makefile 這的檔案囉!通常程式開發者會寫一支 scripts 來檢查您的 Linux 系統、相關的套件屬性等等,這個步驟相當的重要,因為未來您的安裝資訊都是這一步驟內完成的!另外,這個步驟的相關資訊應該要參考一下該目錄下的 README 或 INSTALL 相關的檔案!!基本上,這個步驟完成之後會建立( 或修改 )一個 Makefile ,這就是參數檔啦!
     
  3. make clean

  4. make 會讀取 Makefile 中關於 clean 的工作。這個步驟不一定會有,但是希望執行一下!為什麼呢?因為在進行編譯的時候,會產生一些 *.o 的檔案,例如有個 abc.c 的原始碼,經過編譯後會變成 abc.o 的檔案!我們稱這些檔案為 object file ,這些檔案如果之前已經編譯過並留下來的話,那麼這次再編譯的時候,就不會編譯該檔案,然而由於我們可能已經修改了部分的參數,因此該檔案的編譯結果事實 上應該會有所不同!因此,為了避免前一次留下來的資料可能影響到這次編譯的結果,所以通常可以進行一下這個步驟囉!
     
  5. make

  6. make 會依據 Makefile 當中的預設工作進行編譯的行為!編譯的工作主要是進行 gcc 來將原始碼編譯成為可以被執行的 object files ,但是這些 object files 通常還需要一些函式庫之類的 link 後,才能產生一個完整的執行檔!使用 make 就是要將原始碼編譯成為可以被執行的可執行檔,而這個可執行檔會放置在目前所在的目錄之下,尚未被安裝到預定安裝的目錄中;
     
  7. make install

  8. 通常這就是最後的安裝步驟了,make 會依據 Makefile 這個檔案裡面關於 install 的項目,將上一個步驟所編譯完成的資料給他安裝到預定的目錄中,就完成安裝啦!
     
  9. 特別留意

  10. 請注意,上面的步驟是一步一步來進行的,而其中只要一個步驟無法成功,那麼後續的步驟就完全沒有辦法進行的!因此,要確定每一的步驟都是成功的才可以!舉個 例子來說,萬一今天你在 ./configure 就不成功了,那麼就表示 Makefile 無法被建立起來,要知道,後面的步驟都是根據 Makefile 來進行的,既然無法建立 Makefile ,後續的步驟當然無法成功囉!另外,如果在 make 無法成功的話,那就表示原始檔案無法被編譯成可執行檔,那麼 make install 主要是將編譯完成的檔案給他安裝下去的,既然都沒有成功的執行檔了,怎麼進行安裝?所以囉,要每一個步驟都正確無誤才能往下繼續做!此外,如果安裝成功, 並且是安裝在獨立的一個目錄中,例如 /usr/local/packages 這個目錄中好了,那麼您就必需手動的將這個套件的 man page 給他放到 /etc/man.config 裡面去。

一般 Tarball 套件安裝的建議事項( 如何移除?升級? )
或許您已經發現了也說不定,那就是為什麼前一個小節裡面, Tarball 要在 /usr/local/src 裡面解壓縮呢?呵呵!基本上,在預設的情況下,原本的 Linux distribution 釋出安裝的套件大多是在 /usr 裡面的,而使用者自行安裝的套件則建議放置在 /usr/local 裡面。這是考量到管理使用者所安裝套件的便利性。怎麼說呢?我們曉得幾乎每個套件都會提供線上說明的服務,那就是 info 與 man 的功能。在預設的情況下, man 會去搜尋 /usr/local/man 裡面的說明文件,因此,如果我們將套件安裝在 /usr/local 底下的話,那麼自然安裝完成之後,該套件的說明文件就可以被找到了。此外,如果您所管理的主機其實是由多人共同管理的,或者是如同學校裡面,一部主機是由學生管理的,但是學生總會畢業吧?所以需要進行交接,如果大家都將套件安裝在 /usr/local 底下,那麼管理上不就顯的特別的容易嗎?!所以囉,通常我們會建議大家將自己安裝的套件放置在 /usr/local 下,至於原始碼( Tarball )則建議放置在 /usr/local/src( src 為 source 的縮寫 )底下啊。
 
再來,讓我們先來看一看 Linux distribution 預設的安裝套件的路徑會用到哪些?我們以 apache 這個軟體來說明的話( apache 是 WWW 伺服器軟體,詳細的資料請參考伺服器架設篇。您的系統不見得有裝這個套件 ): 我們會發現套件的內容大致上是擺在 etc, lib, man, bin 等目錄當中,分別代表『設定檔、函式庫、執行檔、線上說明檔』。好了,那麼你是以 tarball 來安裝時呢?如果是放在預設的 /usr/local 裡面,由於 /usr/local 原本就預設這幾個目錄了,所以你的資料就會被放在:

一個簡單的範例、利用 ntp 來示範
讀萬卷書不如行萬里路啊!所以當然我們就來給他測試看看,看您是否真的瞭解了如何利用 Tarball 來安裝軟體呢?!我們利用時間伺服器 ntp-4.1.2 這個套件來測試安裝看看。先請到 http://www.ntp.org/downloads.html 這個目錄去下載檔案,(您也可以下載比較新的檔案來測試的啦!)或者直接到鳥哥的網站下載:
http://linux.vbird.org/download/books/basic/source_code/ntp-4.1.2.tar.gz
假設我對這個套件的要求是這樣的: 那麼您可以依照底下的步驟來安裝測試看看( 如果可以的話,請您不要參考底下的文件資料,先自行安裝過一遍這個軟體,然後再來對照一下鳥哥的步驟喔! )。
 
# 1. 解壓縮,並閱讀一下 ntp 底下的 README 與 INSTALL: 
[root@test root]# cd /usr/local/src 
[root@test src]# tar -zxvf /root/ntp-4.1.2.tar.gz 
# 這個步驟會讓原始碼解開成為 /usr/local/src/ntp-4.1.2 這個目錄
 
# 2. 進入原始碼所在目錄,並且查閱如何安裝的技巧:
[root@test src]# cd ntp-4.1.2 
[root@test ntp-4.1.2]# vi INSTALL ( 或 vi README) 
 
# 3. 開始設定參數、編譯與安裝:
[root@test ntp-4.1.2]# ./configure --help | more 
# 上面這個動作可以察看一下可用的參數! 
 
[root@test ntp-4.1.2]# ./configure --prefix=/usr/local/ntp  \ 
>  --enable-all-clocks --enable-parse-clocks 
checking for gcc... (cached) gcc
checking whether we are using the GNU C compiler... (cached) yes
checking whether gcc accepts -g... (cached) yes
.....(略)....
configure: creating ./config.status
config.status: creating Makefile
# 一般來說 configure 設定參數較重要的就是那個 --prefix=/path 了,
# --prefix 後面接的路徑就是『這個軟體未來要安裝到那個目錄去?』
# 如果您沒有指定 --prefix=/path 這個參數,通常預設參數就是 /usr/local
# 至於其他的參數意義就得要參考 ./configure --help 了!
# 這個動作完成之後會產生 makefile 或 Makefile 這個檔案
# 當然啦,這個偵測檢查的過程會顯示在螢幕上,特別留意關於 gcc 的檢查
# 還有最重要的是最後需要成功的建立起 Makefile 才行!(上面最後一行)
 
[root@test ntp-4.1.2]# make clean ; make 
[root@test ntp-4.1.2]# make check
[root@test ntp-4.1.2]# make install
# 將資料給他安裝在 /usr/local/ntp 底下
 
整個動作就這麼簡單,您完成了嗎?!完成之後到 /usr/local/ntp 您發現了什麼?!

利用 patch 更新原始碼

函式庫管理:
首先我們要知道的是,函式庫的類型有哪些?依據函式庫被使用的類型而分為兩大類,分別是靜態( Static )與動態( Dynamic )函式庫兩類。底下我們來談一談這兩種類行的函式庫吧!

檢驗軟體正確性
前面提到很多升級與安裝需要注意的事項,因為我們需要克服很多的程式漏洞,所以需要前往 Linux distribution 或者是某些套件開發商的網站,下載最新並且較安全的檔案來安裝才行。好了,那麼『有沒有可能我們下載的檔案本身就有問題?』是可能的!因為駭克無所不在,很多的套件開發商已經公布過他們的網頁所放置的檔案曾經被竄改過!那怎麼辦?連下載原版的資料都可能有問題了?難道沒有辦法判斷檔案的正確性嗎?!
 
這個時候我們就要透過每個檔案獨特的指紋驗證資料了!因為每個檔案的內容與檔案大小都不相同,所以如果一個檔案被修改之後,必然會有部分的資訊不一樣!利用這個咚咚,我們可以使用 MD5 這個指紋驗證機制來判斷該檔案有沒有被更動過!舉個例子來說,中山大學提供的 Red Hat 9 原版光碟下載點( http://ftp.nsysu.edu.tw/Linux/RedHat/linux/9/en/iso/i386/ )同時提供了 Red Hat 9 所有光碟 ISO 檔案的 MD5 編碼,透過這個編碼的比對,我們就可以曉得下載的檔案是否有問題。那麼萬一 Red Hat 提供的光碟映象檔(image)被下載之後,讓有心人士偷偷修改過,再轉到 Internet 上面流傳,那麼你下載的這個檔案偏偏不是原廠提供的,呵呵!你能保證該檔案的內容完全沒有問題嗎?!當然不能對不對?!是的,這個時候就有 md5sum 這個檔案指紋的咚咚出現啦!說說他的用法吧!

md5sum

重點回顧
參考資源
如果您對於程式的開發相當的有興趣,那麼真的建議挑這個跨平台的 C 語言來學習!

課後練習
原始碼與 Tarball 套件管理員

2002/08/21: 第一次完成
2003/02/11:重新編排與 加入 FAQ
2004/03/25:原本是 Tarball 與 RPM ,本日開始將 Tarball 與 RPM  分開說明與講解(後續會花好幾天喔!),最重要的是 Source code 的說明,並提到相關的 gcc compile 功能等等!
2004/04/10:經歷了當兵中的無奈生活,終於將這篇給他完工了∼

2002/05/08 以來統計人數


Designed by VBird during 2001-2004.  Aerosol Lab.