《鳥哥的Linux私房菜》筆記
提到Linux,就不得不提GNU和GPL授權所產生的自由軟件(free software)與開放源碼(Open Source)等。
我們將通過Linux操作系統里面的執行文件來理解什么是可執行的程序,以及了解什么是編譯程序。另外,與程序息息相關的函數庫(library)的信息也需要了解一番!
在這節中,希望你可以了解如何將開放源碼的程序設計、加入函數庫的原理、通過編譯而成為可執行的二進制程序(Binary Program),到最后該執行文件可被我們所使用的一連串過程。
開放源碼的軟件安裝與升級簡介
如果我想要在我的Linux主機上面跑web服務(WWW),那我就一定需要安裝web服務器軟件才行。同理,在Linux主機上進行一些有的沒的功能,學會如何安裝軟件就十分重要了。
在Windows操作系統上,安裝軟件點擊“下一步”就行了。不過也由于此,在Windows系統上面的軟件都是一模一樣的,因此你無法修改該軟件的源代碼。萬一你想增加或減少該軟件的某些功能時,大概就只能求助于軟件發行商了。
像目前的病毒、木馬等,都可能對你主機上面的某些軟件造成影響,導致主機出現問題。如果你能夠借由安全信息單位提供的修改方式進行修改,那么你將很快速的自行修補好軟件漏洞,而不必等到軟件發行商提供的補丁。
要知道,提早打補丁是一件很重要的事情。
因為Linux上面的軟件幾乎都是經過GPL的授權,所以每個軟件幾乎均提供源代碼,并且你可以自行修改代碼,以符合你個人的需求!
什么是開放源碼、編譯器與可執行文檔
在討論一個程序代碼是很么之前,我們先來談論一下什么是可執行文件。
在Linux系統上面,一個文件能不能被執行看的是有沒有可執行權限(X)。不過,Linux系統上真正認識的可執行文件其實是二進制文件(binary program),如/bin/touch即為二進制程序代碼。
shell scripts雖然也能夠執行,但它只是利用shell(bash)這個程序的功能來進行一些判斷式,而最終執行的除了bash提供的功能外,仍是調用一些已經編譯好的二進制程序來執行的!當然,bash本身就是一個二進制程序。
利用file命令來查看一個文件是否為binary
如果是binary而且可執行的話,他就會顯示執行文件類別(ELF 64-bit LSB executable),同時會說明是否使用共享庫(uses shared libs)。而如果是一般的script,那它就會顯示 text executable之類的字樣。
事實上,/etc/init.d/network顯示的Bourne-Again shell script這,是因為我們script的#!/bin/bash的原因。
既然Linux操作系統真正認識的其實是binary program,那么我們是如何做出這樣一個二進制程序的呢?
首先,必須要寫程序,一般用文本處理器(如vi)或IDE來進行程序的編寫,寫完的程序就是所謂的程序源代碼啰;
程序源代碼其實就是一般的純文本文件;
在完成源代碼的編寫后,接下來就是將這個文件 “編譯”?稱為操作系統能夠看懂的二進制程序;
編譯自然需要用到“編譯器”來操作,經過編譯程序的編譯與連接之后,生成一個可執行的二進制程序。
舉例來說,在Linux上最標準的程序語言是C,我使用C進行原始程序的書寫。寫完后,以Linux上標準的C語言編譯器gcc這個程序來編譯,就可以制作出一個可執行的二進制程序了。
事實上,編譯過程中還會產生所謂的“目標文件(Object file)”,以 “.o”結尾。C語言的源碼文件通常以“.c”結尾。
此外,有時我們在程序中“調用、引用”其他外部子程序,或者是利用其他軟件提供的函數功能。這個時候,我們就必須在編譯的過程中,將該函數庫加進去。如此一來,編譯程序就可以將所有的程序代碼與函數庫做一個鏈接(Link)以生成正確的執行文件。
開放源碼:就是程序源代碼,寫給人類看的程序語言,但機器并不認識,所以無法執行;
編譯器:將程序源碼編譯成為機器看得懂的語言,類似翻譯者的角色;
可執行文件:經過編譯程序編譯后,變成機器看得懂的二進制程序后稱為可以執行的文件;
什么是函數庫
什么是函數庫?舉個栗子,我們Linux系統上通常已經提供一個可以進行身份驗證的模塊--PAM模塊。這個模塊提供的功能可以讓很多的程序在被執行的時候,除了可以驗證使用者登錄信息外,還可以將身份確認的資料記錄在日志文件里,以方便系統管理員的追蹤。
既然有這么好的功能,那如果我要編寫具有身份認證功能的程序時,直接引用該PAM的功能就好啦,我也不需要重新設計認證機制!也就是說,在我的程序代碼里面,設置去掉用PAM的函數功能,我的程序就可以利用Linux原本的身份認證程序。此外,Linux內核也提供了相當多的函數庫給硬件開發者利用。
函數庫又分為動態函數庫與靜態函數庫。函數庫,類似于子程序的角色,可以被調用來執行的一段功能函數。
什么是make與configure
事實上,使用類似于gcc的編譯器來進行編譯的過程并不簡單。因為一套軟件并不會僅有一個程序,而是一堆程序程序代碼文件。所以除了每個主程序與子程序需要寫上一條編譯過程的指令外,還需要寫上最終的鏈接程序。
這個時候,我們就可以使用make這個命令的相關功能來進行編譯過程的命令簡化了!
當執行make時,make會在當前的目錄下搜尋Makefile(makefile)這個文本文件,而Makefile里面則記錄了源碼如何編譯的詳細信息。make會自動判別源碼是否經過變動了而自動更新執行文件。
make是一個程序,會去找Makefile這個文本文件。通常軟件開發商都會寫一個檢測程序來檢測用戶的操作環境,以及該操作系統是否有軟件開發商所需要的其他功能。該檢測程序檢測完畢后,就會主動建立這個Makefile的規則文件啦。通常這個檢測程序的文件名是configure或者config。
為什么要檢測操作系統?不同Linux版本內核使用的系統調用可能不同,而且每個軟件所依賴的函數庫也不相同。
同時,軟件開發商不會僅針對Linux開發,而是對整個Unix-Like做開發。所以它也必須要檢測該操作系統平臺有沒有提供合適的編譯器!當然就要檢測環境?。?/p>
檢測是否有適合的編譯器,來編譯本軟件的程序源代碼;
檢測是否已經存在本軟件所需要的函數庫,或其他需要的依賴軟件;
檢測操作系統平臺是否適合本軟件,包括Linux的內核版本;
檢測內核的頭定義文件(header include)是否存在,驅動程序必須要檢測。
由于不同的Linux distribution的函數庫文件所放置的路徑,或者是函數庫的文件名定義,或者是預安裝的編譯器,以及內核的版本都不相同。
因此,同一套軟件在不同的平臺上執行時,必須要重復編譯,因此才需要源碼。
什么是Tarball的軟件
如果能夠將這些程序源碼通過打包與壓縮的技術來將文件的數量與容量減小。不但讓用戶容易下載,軟件開發商網絡帶寬也能夠節省很多。這就是Tarball文件的由來。
一個內核的源碼文件大概是幾百M,如果每個人都去下載這樣的一個內核文件,那網絡帶寬會變得很擁擠!
所謂的Tarball文件,其實就是將軟件的所有源碼文件先以 "tar"打包,然后再通過壓縮技術來壓縮。通常是以 gzip來壓縮。
因為利用了 tar 和 gzip 的功能,所以tarball文件一般的文件擴展名為 .tar.gzip 或者是簡寫 .tgz;不過由于 bzip2 與 xz 的壓縮效率較好,所以Tarball漸漸地以 bzip2 和 xz 的壓縮技術來取代 gzip啰。因此文件擴展名也有 .tar.bz2 , .tar.xz之類的。
所以說,Tarball是一個壓縮包,你將它解壓之后,里面的文件通常會有:
源代碼文件(Source Code);
檢測程序文件(configure或config);
本軟件的簡易說明與安裝說明(INSTALL 和 README)。
INSTALL和README文件是很重要的,參考著兩個文件Tarball軟件的安裝就很簡單了。
如何安裝和升級軟件
那么怎么安裝與升級一個Tarball軟件呢?
新版本有新功能;
舊版本有安全漏洞;
舊版本軟件執行效率不佳。
當一個軟件有安全問題時,千萬不要懷疑,趕緊更新?;旧细路椒ㄓ袃深悾?/p>
直接以源碼通過編譯來安裝與升級;
直接以編譯好的二進制程序來安裝與升級。
直接源碼的方式雖然具有很高的彈性,但畢竟麻煩一點。如果Linux distribution廠商能夠針對自己的系統平臺進行編譯等過程,再將編譯好的二進制程序放出的話,由于環境是相同的,那就可以直接在我的電腦上面安裝,省去復雜編譯的過程。
預先編譯好的程序的機制存在于很多distribution,包括Red Hat系統發展的RPM軟件管理機制與yum在線更新模式。Debian使用的dpkg軟件管理機制與apt在線更新模式等等。
一個軟件的Tarball是如何安裝的呢?
1,將Tarball由廠商的網頁上下載下來;
2,將Tarball解壓,生成很多源碼文件;
3,開始以gcc進行源碼的編譯(會產生object files);
4,然后以gcc進行函數庫、主程序、子程序的鏈接,已形成主要的二進制程序;
#3,4步驟可以通過make命令來簡化
5,將上述的二進制程序以及相關的配置文件安裝到自己的主機上。
使用傳統程序語言進行編譯的簡單范例
經過上面介紹之后,你應該比較清楚的知道“源碼、編譯器、函數庫與可執行文件”之間的相關性了。
單一程序:輸出 Hello World
以Linux上最常見的C語言來編寫這個程序。
請確認Linux系統里面安裝了gcc編譯器;
rpm -q gcc
yum groupinstall Development Tools ? ?#安裝開發工具
編輯程序源代碼:
vim hello.c ? ?\\建議帶上C文件后綴名
#include <stdio.h> ? ? \\#號行并不是注釋
int main(void)
{
? ? print("Hello World\n")
}
編譯與執行:
在默認狀態下,我們直接以 gcc 編譯源碼,并且不加上任何參數,則執行文件會被自動設置為 a.out 這個文件名,這個 a.out 就是編譯成功的可執行二進制程序。
如果我們想要產生目標文件(object file)來進行其他動作,并且修改默認的執行文件名稱。
只要編譯生成一個 a.out 二進制文件就好了嘛,為什么還要生成目標文件后再生成可執行文件?接著往下看吧!
主程序、子程序鏈接:子程序的編譯
如果我們在一個主程序里面又調用了另一個子程序呢?這是一個很常見的程序寫法,可以簡化整個程序的可讀性!在下面例子中,我們以 thanks.c 這個主程序去調用 thanks_2.c 這個子程序。
gcc -c thanks.c thanks_2.c ? #編譯產生 目標文件(object file)
gcc -o thanks thanks.o thanks_2.o ? ?#生成可執行文件thanks
現在明白為什么要生成目標文件了吧?由于大多時候我們的源碼文件并非只有一個文件,所以我們無法直接進行編譯。這個時候就需要先生成目標文件,然后再以鏈接制作新的二進制可執行文件即可!
另外,如果以后你對thanks_2.c這個文件的內容作了修改,則你只需要重新編譯thanks_2.c來產生新的thanks_2.o,然后再以鏈接制作出新的二進制可執行文件即可!而不必重新編譯其他沒有改動過的源碼文件。
對于軟件開發者來說,只用重新編譯修改過的文件,這是一個很重要的功能。
調用外部函數庫:加入鏈接的函數庫
如果說要計算數學公式呢?看看下面這個程序。
#include <stdio.h>
#include <math.h>
int main(void)
{
float value;
value = sin(3.14/2);
printf("%f\n",value);
}
gcc sin.c ? ?#新的 gcc 會主動將函數抓來給你用,所以只要加上 include <math.h>就好!
新版的gcc會主動幫你將所需要的函數庫抓進來編譯,所以不會出現報錯信息!事實上,數學函數庫使用的是 libm.so 這個函數庫,最好在編譯的時候將這個函數庫寫進去。
另外,這個函數庫放置的地方是系統默認會去找的 /lib,/lib64,所以你也可與不使用 -L path去指定函數庫目錄。
gcc sin.c -lm -L/lib -L/lib64 ? ?#指定函數庫目錄;
# -l? 加入某個函數庫;
# m? 代表libm.so這個函數庫,其中lib和文件擴展名( .a 或 .so)不需要寫;
#所以 -lm 表示使用libm.so(lib.a)這個函數庫的意思。
# -L/path 從給出的path找尋lib.so。
gcc的簡單用法(編譯、參數、鏈接)
gcc為Linux上面最標準的編譯器,這個gcc是由GNU計劃所維護的。介紹幾個gcc常用的參數。
另外,我們通常稱 -Wall 或者 -O 這些非必要參數為標志(FLAGS),因為我們使用的是C語言,所以有時會稱為CFLAGS。這些變量在make先關用法時很重要!
用make進行宏編譯
make可以簡化編譯過程里面所執行的指令,同時還具有很多方便的功能。
為什么要用make
假設我的執行文件里面包含了四個源碼文件,分別是main.c,haha.c,sin.c,cos.c這四個文件。
main.c ? ?#主要目的讓使用者輸入角度與調用其他三個子程序;
haha.c ? ?#輸出一堆有的沒的信息;
sin.c ? ?#計算輸入角度的sin值;
cos.c ? ?#計算輸入角度cos值。
那么你可能需要這樣編譯:
gcc -c main.c
gcc -c haha.c
gcc -c sin.c
gcc -c cos.c
#生成目標文件(object file),但不生成二進制程序
gcc -o main main.o haha.o sin.o cos.o ? ?#鏈接成為執行文件
./main ? ?#執行可執行文件
編譯的過程需要進行好多動作,而且如果要重新編譯,則上述的流程還得重來一遍。如果可以的話,能不能一個步驟就給它完成所有動作呢?
make工具就是用來簡化gcc編譯流程的。
先試試在目錄下建立一個Makefile的文件
如果我們建立一個shell script來將上面的所有動作都集結在一起,不是具有同樣的效果嗎?
效果不一樣。以上面的測試為例,我們僅寫出了main需要的目標文件,結果make會主動地去判斷每個目標文件相關的源碼文件,并直接予以編譯,最后再直接進行鏈接的的動作!真的很方便!
此外,如果我們更動過某些源碼文件,則make也可以主動的判斷哪一個源碼與相關的目標文件有改動過,并僅更新該文件!如此一來,將可大大的節省很多編譯時間呢!要知道,在程序進行編譯行為時,會消耗很多的CPU資源呢!
所以,make有這些好處:
簡化編譯時所需要執行的指令;
若在編譯完成后,修改了某個源碼文件,則make僅會針對被修改的文件進行編譯,其他的object file不會被更改;
最后可以依照相依性來更新(update)執行文件。
make里面最需要注意就是那個規則文件,也就是Makefile這個文件的語法。
Makefile的基本語法與參數
基本的Makefile規則:
目標(target): 目標文件1 目標文件2...
<tab空格> gcc -o 欲新建的可執行文件 目標文件1 目標文件2...
#特別注意,命令行必須要以tab鍵作為開頭才行
那個目標(target)就是我們想要建立的信息,而目標文件就是具有相關性的object files。要建立可執行文件的語法就是以 <tab>開頭的那一行。
特別注意,命令行必須要以<tab鍵>作為開頭才行。
Makefile基本規則:
#代表注釋;
<tab>需要在命令行(如gcc編譯命令)的只一個字符;
目標(target)與依賴文件(指目標文件)之間需以":"分割。
舉個栗子:
vi Makefile
main: main.o haha.o sin.o cos.o
? ? ? gcc -o main main.o haha.o sin.o cos.o -lm ? ?#不接-L/path表示使用系統默認
clean:
? ? ? rm -f main main.o haha.o sin.o cos.o
如果我們想要建立的是,使用 make main;
如果想要刪除,使用 make clean;
如果想先清除在編譯的話,使用 make clean main。
makefile也可以像shell script那樣,將重復的信息定義為變量,簡化Makefile以方便使用。
與bash shell script的語法有點不太相同,Makefile變量的基本語法為:
1,變量與變量內容以 = 隔開,兩邊可以具有空格;
2,變量左邊不可有<tab>;
3,變量與變量內容 = 兩邊不能具有 : ;
4,在習慣是,變量最好使用大寫字母為主;
5,運用變量時,以 $(變量) 或 ${變量} 使用;
6,在該shell中定義的變量是可以被套用的,如CFLAGS;
7,在命令模式也可以定義變量;
由于gcc在進行編譯行為時,會主動的去讀取CFLAGS這個環境變量,所以可以直接通過shell定義這個環境變量。
CFLAGS="-Wall" make clean main
#這個動作在make進行編譯時,會去取用CFLAGS的變量內容。也可以在Makefile文件里面定義。
環境變量的優先規則:
make指令后面加上的環境變量優先;
Makefile里面指定的環境變量第二;
shell原本具有的環境變量第三。
#注意,$@代表目前的目標(target)
gcc -o $@ ${OBJS} ${LIBS}
Tarball的管理與建議
了解了源碼的相關信息后,再來了解如何使用具有源碼的Tarball(壓縮包)來建立一個屬于自己的軟件了!
從前面幾個小節的說明中,我們知道其實Tarball的安裝是可以跨平臺的,因為C語言的程序在各個平臺上面是可以互通的,只是需要的編譯器可能不相同而已。如Linux有gcc而Windows上也有相應的C編譯器。
如果源碼跨平臺編譯沒有成功呢?只需要修改小部分代碼就可以進行跨平臺的移植了。也就是說,我們在Linux下寫的程序,理論上是可以在Windows上面編譯的。這就是源碼的好處。所以,擴平臺性強大語言也體現了它的優勢。
使用源碼管理軟件所需要的基礎軟件
從源碼的說明我們曉得要生成一個二進制程序需要很多東西的呢!包括如下軟件:
gcc或cc等C語言編譯器(complier);
make及autoconfig等軟件;
需要Kernel提供的Librarys以及相關的Include文件;
按照軟件開發商提供的README與INSTALL文件說明步驟來進行,安裝是容易的。
yum groupinstall Development Tools ? ?#安裝gcc
yum groupinstall X Software Development ? ?#安裝圖形界面軟件
yum groupinstall Legacy Software Development ? ?#安裝較舊的軟件
Tarball安裝的基本步驟
Tarball方式解壓出的軟件是需要重新編譯可執行的二進制文件的。而Tarball是以tar這個命令來打包與壓縮文件。所以,需要先將Tarball解壓,然后 到源碼所在的目錄下進行Makefile的建立,再以make來進行編譯與安裝的動作。
安裝流程基礎操作大多如下:
1,取得源文件:將Tarball文件在/usr/local/src目錄下解壓;
2,取得步驟流程:進入新建立的目錄下面,去查閱INSTALL和README等相關文件的的內容;
3,依賴屬性軟件安裝:根據INSTALL/README的內容查看并安裝好一些依賴的軟件;
4,建立Makefile文件:以自動檢測程序(configure或config)檢測操作系統,并建立Makefile這個文件;
5,編譯:以make這個程序并使用該目錄下的Makefile作為它的參數設置值,來進行make動作;
6,安裝:以make這個程序,并以Makefile這個參數設置值,依據install這個目的(target)的指定來安裝到正確的路徑!
注意第二點,解壓后的文件里面的README一般都會有詳細的解釋。
至于Makefile生成以后,里面會有相當多的目標(target),最常見的就是 install 與 clean。
make將源碼進行編譯。注意了,編譯完成的可執行文件與相關的配置文件都還在此目錄當中哦!因此,最后要進行?make install 來將編譯完成的所有咚咚鏘都給他安裝到正確的路徑里去,最后就可以使用該軟件了!
Tarball軟件大概安裝方式:
1,./configure ? ?#這個步驟是建立Makefile文件啰;
2,make clean ? ?#這個步驟不一定有,它清除object file;
3,make ? ?#make依據Makefile的設置進行編譯;
4,make install ? ?#通常這是最后的安裝步驟了,make依據Makefile文件里的install目標,將編譯完成的資料安裝到指定目錄中去。
上面步驟是一步一步來進行的,只執行某個步驟無法成功,那么后續步驟耶無法進行!因此必須要每一步都成功才行!
一般Tarball軟件安裝的建議事項(如何刪除?升級?)
或許你已經發現了,為什么Tarball要在/usr/local/src里面解壓呢?基本上,在默認的情況下,原本的Linux distribution發布安裝的軟件大多是在 /usr 里面的,而用戶自行安裝的軟件則建議放在/usr/local里面。這是考慮到管理軟件的便利性。
man會去搜尋/usr/local/man 里面的說明文件。因此,將軟件安裝在/usr/local下的話,那么自然安裝完成之后,該軟件的說明就可以被找到了。
建議將自己安裝的軟件放在/usr/local下,源碼則建議放在/usr/local/src下。
來看看Linux distribution默認的軟件安裝路徑會用到哪些?以Apache為例:
/etc/httpd; ? ?#配置文件
/usr/lib; ? ?#函數庫
/usr/bin; ? ?#執行文件
/usr/share/man; ? ?#說明文件
如果你是以Tarball來安裝Apache時,如果放在/usr/local里面,由于/usr/local原本就默認有這幾個目錄,所以你的資料會被放在:
/usr/local/etc ;
/usr/local/bin ;
/usr/local/lib ;
/usr/local/man ;
如果你的每個軟件都選擇在默認的路徑下安裝的話,那么所有的軟件的文件都將放在這四個目錄當中,因此,如果你都安裝在這個目錄下的話,那么未來在想要升級或卸載的時候,就會比較難以查找文件的來源!
而如果你在安裝的時候選擇的是單獨的目錄,例如我將Apache安裝在/usr/local/apache下,那么你的文件目錄就會變成:
/usr/local/apache/etc ;
/usr/local/apache/bin ;
/usr/local/apache/lib ;
/usr/local/apache/man ;
單一軟件的文件都在同一個目錄下,那么要卸載就簡單多了。只要將該目錄刪除即可視為該軟件以被卸載。如 rm -rf /usr/local/apache 就算卸載了這個軟件。
這種方式雖然有利于軟件的卸載,但我們在執行某些指令的時候,與該指令是否存在PATH這個環境參數所記錄的路徑有關。我們/usr/local/apache/bin肯定不在PATH里面。所以執行apache就得要利用絕對路徑了,否則就得將這個/usr/local/apache/bin加入PATH里面去。
除此之外,Tarball在升級的時候也挺困難的,怎么說呢?還是以Apache來說。WWW服務器為了考慮互動性,所以通常會將PHP+MySQL+Apache一起安裝,這樣的話,那每個軟件在安裝的時候都有一定的順序與程序!因為他們之間具有的相關性,所以安裝時需要三者同時考慮到他們的函數庫與相關的編譯參數。
假設今天我只要升級PHP呢?有時候因為只涉及動態函數庫的升級,那么我只要升級PHP即可!其他的部分或許影響不大。但如果PHP需要重新編譯的模塊比較多,那么可能會連帶的,連apache這個程序也需要重新編譯才行,真是有點頭痛。沒辦法,Tarball有優點也有缺點!
由于Tarball在升級與安裝上面具有這些特色,亦即Tarball在反安裝上面具有比較高的難度,所以為了方便Tarball的管理,建議建議:
最好將Tarball的源碼解壓到/usr/local/src下;
安裝時,最好安裝到/usr/local這個默認路徑下;
考慮未來的反安裝步驟,最好可以將每個軟件單獨的安裝在/usr/local下;
未安裝到單獨目錄的軟件的man page加入 man path搜索
老實說,時至今天真的不太需要tarball的安裝了!
一個簡單的例子,利用ntp來示范
如何利用Tarball來安裝ntp(network time protocol)這個軟件。
http://www.ntp.org/downloads.html? ? #下載ntp
假設我的要求如下:
下載的ntp.xxx.tar.gz文件放在/root目錄下;
源碼要解壓到/usr/loca/src下;
軟件安裝到/usr/local/ntp目錄中;
解壓下載的ntp的Tarball,并參考README/INSTALL文件:
cd /usr/loca/src
tar -zxvf /root/ntp.xxx.tar.gz ? ?#解壓到此目錄
cd ./ntp.xxx
cat README or cat INSTALL?
檢測configure支持的參數,并建立Makefile規則文件:
[ ntp.xxx ] ./configure --help | more ? ?#查詢可用參數有哪些
#--prefix=PREFIX ? ? install architecture-independent files in PREFIX;
#--prefix=/path ? ?表示軟件要安裝到哪個目錄去,如果沒有指定,則默認在/usr/local;
#--enable-all-clocks ? ? + include all suitable non- PARSE clocks:
#--enable-parse-clocks ? ? - include all suitable PARSE clocks:
#特別要留意關于gcc的檢查,最后需要成功的建立Makefile文件才行
最后開始編譯與安裝:
make clean;make
make check
make install
完成之后,你可以看看/usr/local/ntp看看。
利用patch更新源碼
如何更新以Tarball方式安裝的軟件呢?
事實上,當我們發現一些軟件的漏洞,通常是某一段代碼寫的不好所導致的。因此,所謂的“更新源碼”常常是更改部分文件的部分內容而已。既然如此,我們是否可以就那些被更改的文件來進行修改即可?
這有什么好處呢?首先,沒有更改過的文件的目標文件(object file)根本就不需要重新編譯,而且有改動多的文件又可以利用make來自動update(更新)。如此一來,我們原先的設置(Makefile文件里的規則)將不需要重新修改或檢測??梢怨澥『芏鄷r間。如果將舊版的源碼文件改寫成新的版本,那么就能直接編譯而不需要全部的新版本的Tarball重新下載一遍。
利用 diff 命令可以對比兩個文件之間的差異性,我們也知道了新舊版本源碼之間的差異,然后再利用 patch 命令來更新舊版文件。
很多軟件開發商在更新了源碼之后,幾乎都會放出所謂的 patch file,也就是直接將源碼update的一個方式。
假設我有old file 和 patch file:
main-0.1.tgz ? ?#main的0.1版本
main-0.1-to-0.2.patch ? ?#將main由0.1升級到0.2的patch file
將兩者解壓到/root下:
tar -zxvf main-0.1.tgz
cd main-0.1
make clean
./main ? ?#測試舊版功能
查看patch file內容:
上面有底線那部分,代表使用diff去比較時,兩個文件所在路徑,這個路徑非常重要。
patch基本語法:
patch -p數字 < file.patch
#-p后面的數字代表 拿掉file.patch第一行里面的幾個 "/" 線
特別留意那個『 -p數字』,那是與patch_file 里面列出的檔名有關的資訊。假如在 patch_file 第一行寫的是這樣:
*** /home/guest/example/expatch.old
那么當我下達『 patch -p0 < patch_file 』時,則更新的文件是『/home/guest/example/expatch.old 』;
如果『 patch -p1 < patch_file』,則更新的文件為『home/guest/ example/expatch.old』;
如果『patch -p4 < patch_file』則更新『expatch.old』;
也就是說,-pxx那個xx代表『拿掉幾個斜線(/)』的意思!
好了,根據剛剛上面的信息,我們可以發現比較的文件是在main-0.1/xxx與main-0.2/xxx 。所以說,如果你是在main-0.1底下,并且想要處理更新時,就得要拿掉一個目錄(因為并沒有main-0.2的目錄存在,我們是在當前的目錄進行更新的!),因此使用的是-p1才對喔!
更新源碼,并且重新編譯程序:
只要下載了patch file, 就能夠對你的軟件源碼進行更新。只不過更新了源碼并非軟件就更新。你還要將該軟件進行編譯安裝后才行。因為patch的功能只是更新源碼文件。
patch -R < ../main-0.1-to-0.2.patch ? ?#還原
函數庫管理
在Linux系統中,函數庫是很重要的一個項目。因為很多軟件之間都會互相調用彼此提供的函數庫來進行特殊功能的運行,如驗證身份的PAM模塊,網絡連接加密的SSL函數庫。
動態函數庫與靜態函數庫
依據函數庫的使用類型而分為靜態函數庫(Static)與動態函數庫(Dynamic)。
靜態函數庫的特點:
擴展名(.a):這類函數的擴展名為 libxxx.a;
編譯行為:這些函數庫在編譯的時候會直接整合到執行程序中,所以利用靜態函數庫編譯成的文件會比較大;
獨立執行的狀態:這類函數庫最大的優點,就是編譯成功的可執行文件可以獨立執行,而不需要再向外部要求讀取函數庫的內容;
升級難易度:雖然執行文件可以獨立執行,但因為函數庫是直接整合到執行文件中,因此若函數庫升級時,整個執行文件必須重新編譯才能將新版的函數庫整合到程序中。也就是說,只要函數庫升級了,所有將函數庫納入的程序都需要重新編譯。
動態函數庫的特點:
擴展名(.so):擴展名通常為libxxx.so的類型;
編譯行為:動態函數庫在編譯的時候,在程序里面只有一個 “指向(pointer)” 的位置而已。也就是說,動態函數庫的內容并沒有被整合到執行文件中,而是當執行文件要使用到函數庫的機制時,程序才會去讀取函數庫來使用。由于執行文件中僅具有指向動態函數庫所在的指向而已,并不包含函數庫的內容,所以他的文件會比較小一點。
獨立執行的狀態: 這類函數庫編譯出來的程序不能被獨立執行,因為當我們使用到函數庫機制時,程序才會去讀取函數庫,所以函數庫文 必須要存在才行。而且, 函數庫所在的目錄和文件名也不能改變。
升級難易度:雖然此類執行文件無法獨立運行,然而由于是具有指向的功能,所以,當函數庫升級后,執行文件根本不需要進行重新編譯的行為,因為執行文件會直接指向新的函數庫文件。
目前Linux distribution比較傾向于使用動態函數庫,因為如同上面提到的最重要的一點,就是函數庫的升級方便!
由于Linux系統里面的軟件依賴性太復雜了,如果使用太多的靜態函數庫,那么升級某一個函數庫時,就會對整個系統造成很大的影響!因為其他依賴的執行文件也要重新編譯!這個時候動態函數庫就有用多了,因為只要動態函數庫升級就好,其他無需變動。
絕大多數的函數庫都放在 /lib64,/lib目錄下!
此外,Linux系統里面很多的函數庫是由Kernel提供的。那Kernel的函數庫放在哪里呢?就放在/lib/modules里面。
注意, 不同版本的內核提供的函數庫差異性很大,很容易由于函數庫的不同而導致很多原本可執行的軟件無法順利運行!
idconfig與/etc/ls.so.conf
如果我們將常用的動態函數庫提前加載到高速緩存(Cache)中,如此一來,當軟件要取用動態函數庫時,就不需要從頭由硬盤里面讀出來啰。這樣就大大增加了動態函數庫的讀取速度!沒錯,這個時候就需要Idconfig與/etc/ld.so.conf的幫助了。
如何將動態函數庫加載到高速緩存中?
首先必須在/etc/ld.so.conf里面寫下“想要加載進內存當中的動態函數庫所在的目錄”,注意是目錄不是文件;
接下來則利用ldconfig這個執行文件將/etc/ld.so.conf的信息讀入高速緩存中;
同時也將信息記錄一份在/etc/ld.so.cache這個文件中!
事實上,ldconfig還可以用來判斷動態函數庫的鏈接資訊。如你想將目前你系統下的mariadb函數庫加入到高速緩存中時,你可以:
程序的動態函數庫解析:ldd
如何判斷某個可執行的binary文件含有什么動態函數庫呢?很簡單,利用ldd就可以曉得!
例如我想知道/usr/bin/passwd這個程序含有的動態函數庫有哪些,可以這樣做:
如果你常常升級安裝RPM的軟件時,應該常常會發現那個“依賴屬性”的問題。沒錯,我們可以先以ldd來觀察“相依賴函數庫”之間的相關性。如上面的libc.so.6其實還跟ld-linux-x86-64.so.2有關。
檢驗軟件的完整性
有沒有可能我們下載的文件本身就有問題,因為cracker無處不在。這個時候我們就要通過每個文件讀他的指紋驗證數據了!我們可以利用MD5/sha1或更嚴密的sha256等指紋驗證機制來判斷該文件有沒有被更動過。
md5sum / sha1sum / sha256sum
目前有多種機制可以計算文件的指紋碼,我們選擇較為廣泛的MD5,SHA1或SHA256 加密機制來處理。
此處用前面談到的ntp軟件來檢查看看。首先看一下ntp軟件的版本,其官網上有提供md5sum的數據。
如何確認我們下載的文件是正確沒問題的呢?這樣處理一下:
一般而言,每個系統里面的文件內容大概都不相同,如你的系統中的/etc/passwd這個登錄信息與我的不一樣,所以又md5sum這個文件志文分析程序所自行計算出來的指紋表當然就不相同啰!
那么如何應用這個東西呢?基本上,你必須要在Linux系統上為你的這些重要的文件進行指紋數據庫的建立(好像在做戶口調查)。
將下面這些重要文件建立指紋數據庫:
/etc/passwd;
/etc/shadow;
/etc/group;
/usr/bin/passwd;
/sbin/rpcbind;
/bin/login;
/bin/ls;
/bin/ps;
/bin/top;
這幾個文件最容易被修改了!因為很多木馬程序執行的時候,還是會有所為的“執行程序PID”,為了怕被root追查到,所以他們都會修改這些檢查進程的文件,如果你可以替這些文件建立指紋數據庫(就是使用md5sum)檢查一次,將該文件指紋記錄下來,然后常常以shell script的方式由程序自行來檢查指紋是都相同),那么對于文件系統來說會比較安全啦!
重點回顧:
源碼大多是純文本,需要通過編譯器編譯后,才能轉換成Linux系統能夠識別的可執行的二進制文件;
開放源碼可以加速軟件的更新速度,讓軟件效能更快、漏洞修補更及時;
在Linux系統中,最標準的C語言編譯器為gcc;
在編譯的過程中,可以由其他軟件提供的函數庫來使用該軟件的相關機制與功能;
為了簡化編譯過程中復雜的指令輸入,可以由make與Makefile規則定義,來簡化程序的更新、編譯與鏈接等動作;
Tarball為使用tar與gzip/bzip2/xz壓縮功能所打包與壓縮的,具有源碼的文件;
一般而言,要使用Tarball管理Linux系統上的軟件,最好需要gcc,make,autoconfig,Kernel source,Kernel header等前驅軟件才行。所以在安裝Linux之初,最好就能夠選擇Software development 以及Kernel development之類的群組;
函數庫有動態與靜態兩種。動態函數庫在升級上具有較佳的優勢,動態函數庫擴展名為 .so,靜態則是則是直接被整合到執行程序中,擴展名為 .a;
patch的主要功能在更新源碼,所以更新源碼之后,還需要進行重新編譯安裝等動作;
可利用 ldconfig 與 /etc/ld.so.conf, /etc/ld.so.conf.d/*.conf 來制作動態函數庫的鏈接與存??;
通過?md5sum,sha1sum,sha256sum 的編碼,可以判斷下載的文件是否為原廠商放出且未被篡改的正確文件。同理,可用于校驗本機文件。