FreeBSD
在 FreeBSD下的可執行程序通常可分為兩類,一類為使用各種解釋語言編寫的腳本,如sh、awk、perl、Tcl等,這些程序需要解釋程序進行解釋執行,小巧方便,對於實現不常使用、不要求效率的程序非常有用;另一類就是使用C等高級語言編譯後產生的可執行二進制程序。
1) 解釋腳本程序使用 file命令可以確認文件的類型,包括可以確定一個程序是可直接執行的二進制程序,還是解釋腳本程序。
$ file a.out
a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (FreeBSD), dynamically
linked (uses shared libs), not stripped
$ file /etc/rc
/etc/rc: Bourne shell script text executable
Unix之所以功能強大,原因之一就在於它提供了強大的再開發能力。這不僅與提供了高級語言C的編譯器有關,而且也與提供了很多種能以解釋方式執行的簡單腳本語言有關。解釋程序腳本的特點是方便性、簡單靈活,而且也比較容易學習入手。很多情況下,需要完成的工作任務功能比較單一,並不需要頻繁運行,而且要求快速編寫出來,這就適合使用解釋型語言編寫,並且解釋程序本身就具備處理文本和字符串的便捷性,並能夠和很多現有程序通過系統提供的管道、環境變量等方式結合起來,使得它們非常適合實現文本處理功能。
解釋語言的缺點是每次運行程序時都要載入語言的解釋器,解釋執行程序,因而效率較低,並且不能直接操縱內存和 I/O設備,不適合編寫大型程序和對效率要求較高的場合。
每個解釋腳本程序的第一行指出該腳本程序使用的解釋器,例如一個普通的 shell程序的第一行為:
#! /bin/sh
不同的解釋語言可用在不同的方面,最常用的有 shell解釋程序,依據使用shell的不同,也分為不同的shell腳本,基本上也分為sh和csh兩種不同的風格。系統管理中經常使用shell程序來執行一些日常管理任務,很多軟件也使用shell程序來提供輔助安裝和設置任務。perl也是一種常用的、功能強大的解釋語言,它兼有解釋性程序的方便性和高級編程語言的強大功能,使程序員能在很短的時間內寫出非常有效的程序。因此perl得到了眾多程序員的支持,通過為perl開發了更多的程序模塊,進一步使得perl的處理能力變得更為強大。當前perl已經成為了最流行的一種解釋語言,尤其在編寫Web服務器上的CGI程序方面,更是處於無可爭議的地位。Tcl/tk是另一種解釋語言,它能用在X Window系統下,使用描述語言顯示不同的X控件,因此很多X應用程序使用它來建立自己的圖形接口。
2) 二進制執行程序使用高級語言編寫、並經過編譯得到的二進制執行程序執行效率更高,並且只有二進制格式的執行文件才能充分利用 Unix系統提供的全部功能。同樣系統內核也是一個特殊格式的二進制執行文件。
早期的 Unix使用a.out格式作為它的執行文件格式,隨著Unix的發展,又出現了其他幾種執行文件的格式,當前最重要的執行文件格式為ELF格式,采用這種格式的最初想法是為了在不同平台間采用相同的執行文件格式,並實現動態共享連接庫。雖然ELF文件格式並沒有達到AT&T最初設想的全部目的,但這種文件格式卻成為最流行的執行文件格式。除此之外,實際使用的文件格式還有一種較老的COFF格式,這種格式是在Unix System V R3.2中使用的,當前只有老版本的SCO Unix中還在使用它,而SCO也正逐漸轉向ELF格式。
FreeBSD可以同時支持這兩種執行文件的格式,FreeBSD 2.2.x之前的版本使用a.out格式作為缺省的執行文件格式,到FreeBSD 3.x之後ELF格式成為缺省的執行文件格式,並且以後會徹底轉向ELF。事實上在FreeBSD下的a.out格式具備了相當多的特性,如動態連接等ELF格式具備的特性,也有一些ELF格式不具備的特性,如壓縮執行文件格式。但由於FreeBSD中使用的編譯器gcc決定不再支持a.out格式的緣故,因此FreeBSD也必須轉向ELF格式。這也是當前還支持a.out格式的FreeBSD版本缺省使用較老版本編譯器的原因之一。
在 FreeBSD中,a.out格式的執行文件可以支持壓縮執行格式,這使得使用gzip壓縮過的a.out格式的執行文件也能立即執行。ELF格式的程序不提供這種支持。
FreeBSD的文件格式從aout到ELF的轉變是漸變的,首先是在3.0-RELEASE中將執行程序的缺省格式轉變為ELF格式,內核文件還保持aout格式,直至FreeBSD-3.1,全部執行文件格式才缺省設置為ELF格式。
轉向 ELF也造成很多相關程序的轉變,如原有的Boot Loader不支持ELF格式的內核,3.1-RELEASE就升級到新Boot Loader;而原有的可加載模塊lkm為aout格式,也需要轉向ELF格式的modules。新可加載模塊的位置為/modules目錄,並使用kldload、klduload、kldstat來進行管理。(aout格式的模塊管理命令為modload、modstat和modunload)。
a.out和ELF格式使用的庫文件也是不同的,使用ELF執行文件格式的FreeBSD 3.x中,/usr/lib下為ELF格式的函數庫,而用一個子目錄/usr/lib/aout存放a.out格式的函數庫,用於兼容2.2.x之前版本的FreeBSD程序。但這給一些使用包裝技術的軟件(一些中文外掛系統)造成了一些小麻煩。對不同格式的執行文件要使用不同的包裝庫,系統不會將與程序本身格式不同的連接庫連接到程序上,對應的錯誤信息為 “ bad magic ” ,指出文件格式的不同。
由於 3.x之後的缺省格式為elf格式,為了生成a.out格式的文件,必須在編譯和連接時使用 -aout參數,告訴編譯器gcc和連接器ld使用不同的格式生成執行文件。
3) 靜態連接和動態連接在操作系統發展的早期,除了內核提供的接口,所有的庫函數都要連接到程序中,這樣所有的程序都可以直接在系統內核下運行。然而事實上大部分程序都會使用一些相同的庫函數,尤其是在使用高級語言編程的時候,通常都使用同樣的庫。例如, C語言編寫的程序通常都使用printf函數進行輸出,使用scanf讀入用戶輸入內容。如果每個庫函數都連接到用戶程序中,這樣每個程序都會包括這個函數的一個拷貝,就浪費了內存空間。
因此,現代操作系統使用動態連接的技術,不將常用的庫直接編譯進每個程序中,而是保留相應的接口,在內核載入程序時,再使用動態連接程序將庫載入並和執行程序連接起來。這就是動態連接的技術,由於庫和程序是分別載入的,因此多個程序可以共享一個庫的同一個拷貝,節約了資源。
不論對於 a.out格式還是ELF格式,FreeBSD均支持動態連接,因此應用程序缺省就使用動態連接的方式。如果想使用靜態連接,可以在應用程序編譯連接時,指定-static連接選項,將目標程序連接成靜態連接的執行文件。由於庫代碼被連接進執行文件中,靜態連接的執行文件要比動態連接的執行文件要大。
QUOTE:
$ cc -static -o a1 hello.c
$ cc -o a2 hello.c
$ ls -l a1 a2
-rwxr-xr-x 1 wb wheel 45017 Apr 18 16:26 a1
-rwxr-xr-x 1 wb wheel 2540 Apr 18 16:27 a2在 FreeBSD下,共享庫被放到/etc/ld-config設定的目錄下,通常為/usr/lib,每個庫文件使用.so和庫的版本號結尾。例如,libc.so.3.1為一個標准C庫函數的動態共享庫文件。對於a.out格式的執行文件,其動態庫文件位於/usr/lib/aout目錄下。
可以使用程序 ldd來確定一個程序使用的動態連接庫:
QUOTE:
bash-2.02$ ldd /usr/bin/vi
/usr/bin/vi:
libcurses.so.2 => /usr/lib/libcurses.so.2 (0x2808e000)
libtermcap.so.2 => /usr/lib/libtermcap.so.2 (0x2809a000)
libc.so.3 => /usr/lib/libc.so.3 (0x2809f000)