Apache HTTP 服務器是一個模塊化(或說積木式)的程序,管理員可以選擇一些模塊來增加服務器的某些功能。這些模塊,可以在創建服務器程序時靜態地編譯到httpd服務器的二進制代碼中,也可以編譯成一些獨立於服務器程序的Dynamic Shared Objects (DSOs)文件。D
Apache HTTP 服務器是一個模塊化(或說積木式)的程序,管理員可以選擇一些模塊來增加服務器的某些功能。這些模塊,可以在創建服務器程序時靜態地編譯到httpd服務器的二進制代碼中,也可以編譯成一些獨立於服務器程序的Dynamic Shared Objects (DSOs)文件。DSO 文件可以在編譯服務器程序時創建,也可以在以後利用Apache擴展工具apxs來單獨創建。 這篇文檔,將描述如何使用DSO 模塊,以及其背後的原理。
實現 Apache HTTPD對DSO 的支持,即對單個模塊的動態加載,是基於一個叫mod_so的模塊來實現的,此時mod_so必須被靜態地編譯到HTTP服務器內核中。這是除了core以外唯一不能以dso方式編譯的模塊。實際操作時,其它的Apache模塊可以在編譯服務器程序時通過單獨指定來將其編譯為DSO文件,正如安裝文檔中講述的,此時configure的設置參數應為--enable-xxxx=shared(xxxx為模塊的名字,如rewrite等)。 當一個模塊被編譯為一個名為mod_foo.so的DSO文件後,就可以在httpd.conf文件中用mod_so的LoadModule命令,告訴服務器在啟動或重新啟動時將此模塊加載。
為了簡化創建Apache模塊(尤其是第三方模塊)的DSO文件的過程,apache提供了一個新工具名叫apxs(APache eXtenSion)。它可以脫離apache的源碼將模塊編譯成DSO文件。它的實現思路非常簡單: 在安裝Apache時,configure腳本的 make install 過程會安裝Apache的C頭文件,並在apxs程序(apxs是一個perl腳本)中對依賴於具體平台的編譯器和連接器設置一些標志(Flag),以供創建DSO文件。通過這種方式,用戶就可以利用apxs在沒有Apache源碼樹且無需針對當前平台的編譯器和連接器進行配置(以生成DSO格式目標文件)的情況下編譯Apache模塊了。
使用概要說明 創建和安裝一個 Apache發布的(distributed) 模塊,比方說將mod_foo.c編譯成mod_foo.so:
$ ./configure --prefix=/path/to/install --enable-foo=shared
$ make install
創建和安裝一個第三方的Apache模塊,比方說將mod_foo.c編譯成mod_foo.so:
$ ./configure --add-module=module_type:/path/to/3rdparty/mod_foo.c --enable-foo=shared
$ make install
為以後安裝(非HTTPD編譯時安裝)模塊配置Apache:
$ ./configure --enable-so
$ make install
(要編譯全部Aapache模塊,用./configure --enable-mods-shared=all --with-egd --with-devrandom --enable-so,但對experimental一類的模塊,需要特別指定,如./configure --enable-mods-shared=all --with-egd --with-devrandom --enable-so --enable-cache=shared --enable-disk_cache=shared --enable-mem_cache=shared --enable-proxy=shared --enable-proxy_connect=shared --enable-proxy_ftp=shared --enable-proxy_http=shared --enable-file_cache=shared --enable-charset_lite=shared --enable-case_filter=shared --enable-case_filter_in=shared --enable-ssl=shared 。具體有哪些模塊可以編譯而沒有打開編譯開關,可在前面的那個configure運行的最後看看哪一個是no.)
利用apxs在沒有Apache源碼樹的情況下,創建和安裝第三方的Apache模塊:
$ cd /path/to/3rdparty
$ apxs -c mod_foo.c
$ apxs -i -a -n foo mod_foo.la
在所有的情況下,當一個模塊編譯完成後,必須在httpd.conf使用LoadModule命令在告訴 Apache激活這個模塊.
背景知識 在現代的
Unix的派生版本中,有一種非常好的機制,叫做Dynamic Shared Objects (DSO) 的動態連接/和加載,它提供了一種方法,將一段代碼編譯成一種特殊格式後可在一個可執行程序運行時將這段程序加載到它的地址空間中。
這種加載通過可以通過兩種方式做到: 在一個可執行程序開始運行後通過一個叫ld.so的系統程序自動加載,或在可執行程序內部通過Unix加載器(loader)的系統編程接口的系統調用dlopen()/dlsym()來手工加載。
在第一種方式中,DSO通常被叫作共享庫或DSO庫,以libfoo.so或libfoo.so.1.2的形式命名。它們被存放在系統目錄中(通常是/usr/lib),它們與可執行程序的聯系是在編譯這個可執行程序時,通過傳遞給連接器一個-lfoo參數來建立的。這裡對庫的引用直接編碼在可執行程序中,因而在程序運行時,Unix加載器會通過以下途徑尋找libfoo.so:在系統目錄/usr/lib下,在通過-R連接參數傳遞給連接器後被編碼在可執行程序中的路徑中,在通過環境變量LD_LIBRARY_PATH指定的目錄中。加載器會解析出來那些在可執行程序中使用但是在DSO定義的標志(symbol)。
可執行程序中的標志,通常不會被DSO引用(因為它是一個可重用的基本代碼庫),因而就不再進行進一步的解析。可執行程序自己不需要做任何事情就可以使用來自DSO的標志,因為Unix加載器已經做了相關的解析工作。(實際上,加載ld.so的代碼是使用動態庫的可執行程序啟動代碼的一部分). 動態加載基本代碼庫的優點是很明顯的:庫的代碼只需在一個系統庫(如libc.so)中保存一次,這樣可以節省每個程序的磁盤空間。
在第二種方式中,DSO通常被叫作共享對象或DSO文件,命名它們時可以使用任意的擴展名,雖然規范的命名是foo.so的樣子。這些文件通常存放在程序所在的目錄(或子目錄)中,它們與執行它們的程序沒有自動建立的聯系。相反,可執行程序在運行時通過dlopen手工將DSO加載。此時,來自DSO供執行程序用的標志沒有被解析。相反,Unix加載器自動解析所有DSO中使用的來自可執行程序和它已經加載的DSO 庫的標志(尤其是來自無處不在的libc.so的所有標志)。在這種方式中,DSO取得可執行程序的標志集合的信息,就象是被靜態地連接到可執行程序中一樣。
最後,為利用DSO's API,可執行程序須通過dlsym()來解析來自DSO的特定標志,供在以後的分派表等處使用。也就是說:可執行程序要想使用來自DSO的標志,須手工解析它。這樣一種機制的優點,不需要的程序段不必加載(因而可以節省內存的使用) ,直到我們討論的程序需要它時。當需要時,這些程序段可以被動態的加載,來擴展基本程序的功能。
雖然DSO機制聽起來簡單,用起來只少有一個比較困難的步驟,就是當用DSO來擴展程序的功能時(第二種方式)時,對來自可執行程序、供DSO使用的標志的解析。為什麼呢? 因為"反向解析" DSO 使用的來自可執行程序標志集合的標志是與函數庫的設計相逆的 (函數庫沒有任何關於那些使用它的程序的信息),而且沒有平台提供這種功能,這也不是一個標准化的功能。實際上,可執行程序的全局標志常常不能再次輸出,因而也無法供DSO使用。要利用DSO來動態擴展一個程序的功能,找到一種方法來強制連接器輸出所有全局標志是必須解決的主要問題。
共享庫的方法就是一個典型,因為它是DSO機制最初設計的目標,因而它被用於操作系統提供的幾乎所有類型的函數庫中。從另一方面來說,利用共享對象來擴展功能的程序並不是很多。
到1998年,只有很少的軟件包在程序運行時利用DSO機制來擴展它們的功能:Perl 5(通過它的XS機制和DynaLoader模塊), Netscape Server,等。從版本1.3開始, Apache 也加入了這個群體,因為Apache早就用模塊的概念來擴展它的功能,且在內容利用基於分派鏈(dispatch-list-based )的方法來將外部的模塊連接到Apache的核心功能中。因此,Apache實際上注定要用DSO來在運行時加載模塊的。
長處和不足 上述的基於DSO的功能有以下優點:
服務器在運行時更加靈活,因為實際的服務器進程可以通過配置文件的LoadModule命令動態的組配,而不必在編譯時通過配置參數指定。例如,通過這種方式,雖然只安裝了一次,但服務器可以以多種形態運行(如標准版本或SSL版本,簡化版本或增強版本[含mod_perl,
php3等])
即使在安裝完成後,服務器仍可以很方便地用第三方的模塊進行功能擴展。這至少對銷售商的軟件支持有幫助,他們可以創建一個apache的核心程序包,和一個包含
PHP3, mod_perl, mod_fastcgi等擴展功能的擴展包。
更容易地
開發 Apache模塊原型,因為借用DSO和apxs,你可以脫離Apache的源碼而只需apxs -i命令就可以開發和編譯模塊,利用apachectl restart 命令,就可以讓新開發的模塊在在apache服務器中運行。
DSO 也有下列不足 DSO並不是在任何平台都可以使用,因為有一些平台不支持動態將一段代碼加載到另一個程序的地址空間中
服務器在啟動時慢大約20%,因為加載器要作標志解析。
在運行時,在有些平台上服務器大約慢5%,因為PIC( position independent code )代碼需要更復雜的組配技巧來解決相對地址的問題,而對絕對地址的情況沒有這種開銷所以會快一些。
因為並不是所有的平台都支持DSO模塊連接(ld -lfoo)其它基於DSO的庫(如基於out的平台通常不提供這個功能而基於ELF的平台則可以),所以不能將DSO機制用於所有類型的模塊。換句話說,可以編譯為DSO文件的模塊限制為那些:引用的標志只來自apache內核、來自C函數庫(libc)、來自其它的Aapche內核使用的動態或靜態庫、或含有PIC代碼的靜態庫文件(libfoo.a)。使用其它代碼的唯一機會,要麼確定apache內核已經對此引用,要能自己通過dlopen()加載代碼。