感謝您耐著性子看了我上一篇有些胡亂的序言--由這次開始我們轉到Apache技術上來--我之所以要把Apache的DSO模式放到第一次闡述的內容裡--是因為在網上的中文Apche技術資料裡基本沒有這方面的內容--而它在Apache的體系結構中占據越來越重要的地位(在Apache1.3.17 for win32版本中-DSO已經是默認的安裝模式了)。 那麼DSO究竟是什麼?事實上DSO是Dynamic Shared Objects(動態共享目標)的縮寫,它是現代Unix派生出來的操作系統都存在著的一種動態連接機制。它提供了一種在運行時將特殊格式的代碼,在程序運行需要時,將需要的部分從外存調入內存執行的方法。Apache在1.3以後的版本後開始支持它。因為Apache早就使用一個模塊概念來擴展它的功能並且在內部使用一個基於調度的列表來鏈接擴展模塊到Apache核心模塊.所以,Apache早就注定要使用DSO來在運行時加載它的模塊。 在mod_ssl和mod_rewrite的作者Ralf S. Engelschall(在我看來他是真正的Apache大師和主要開發者之一,他的個人主頁上 的名篇“Apache 1.3 Dynamic Shared Object (DSO) Support” 中對DSO模式的原理有著比較詳盡的敘述(本文基本基於此)。 讓我們先來看一下Apache本身的程序結構:這是一個很復雜的四層結構--每一層構建在下一層之上。 第四層是用Apache模塊開發的第三方庫--比如open ssl一般來說在Apache的官方發行版中這層是空的,但是在實際的Apache結構中這些庫構成的層結構肯定是存在的。 第三層是一些可選的附加功能模塊--如mod_ssl,mod_perl。這一層的每個模塊通常實現的是Apache的一個獨立的分離的功能而事實上這些模塊沒有一個是必須的,運行一個最小的Apache不需要任何一個此層的模塊。 第二層是Apache的基本功能庫-這也是Apache的核心本質層--這層包括Apache內核,http_core(Apache的核心模塊),它們實現了基本HTTP功能(比如資源處理(通過文件描述符和內存段等等),保持預生成(pre-forked)子進程模型,監聽已配置的虛擬服務器的TCP/IP套接字,傳輸HTTP請求流到處理進程,處理HTTP協議狀態,讀寫緩沖,此外還有附加的許多功能比如URL和MIME頭的解析及DSO的裝載等),也提供了Apache的應用程序接口(API)(其實Apache的真正功能還是包含在內部模塊中的,為了允許這些模塊完全控制Apache進程,內核必須提供API接口),這層也包括了一般性的可用代碼庫(libap)和實現正則表達式匹配的庫(libregex)還有就是一個小的操作系統的抽象庫(libos)。 最低層是與OS相關的平台性應用函數,這些OS可以是不同現代UNIX的變種,win32,os/2,MacOS甚至只要是一個POSIX子系統。 在這個復雜的程序結構中有趣的部分是---事實上第三層和第四層與第二層之間是松散的連接,而另一方面第三層的模塊間是互相依賴的--因這種結構造成的顯著影響就是第三層和第四層的代碼不能靜態地連接到最低層平台級的代碼上。因此DSO模式就成了解決它的一種手段。結合DSO功能,這個結構就變得很靈活了,可以讓Apache內核(從技術上說應該是mod_so模塊而不是內核)在啟動時(而不是安裝時)裝載必要的部分以實現第三層和第四層的功能。 DSO在程序運行時將需要的部分從外存調入內存執行存取通常會有兩種途徑:一種是ld由系統在程序開始時自動載入,這種載入可以由兩種途徑實現:一種是自動由系統在可執行程序開始時調用ld.so來執行。另一種是手動,是通過在執行程序裡程序系統界面到Unix裝載程序通過系統調用tdlopen()/dlsym()來執行的. 那麼具體來說Apache是怎麼實現DSO功能的呢?DSO支持調用特別的Apache模塊是基於一個名叫mod_so.c的模塊,這個模塊必須靜態的編譯Apache的核心。這是除了http_core.c以外僅有的模塊不可以被放到DSO自己(bootstrapping!)。 實際上所有的別的發布的Apache模塊都可以被放置到DSO,通過個別的通過設置被DSO建立允許--允許可能的-共享設置(看頂層的INSTALL文件)或者通過改變AddModule命令在你的src/Configuration到SharedModule命令(看src/INSTALL文件)。 在編譯一個模塊到一個名字叫mod_foo.so的DSO以後,你能夠使用mod_so的LoadModule命令在你的httpd.conf文件裡在服務程序開始時或重新啟動以後調用這個模塊。 為了簡化這種為了Apache模塊而創建DSO文件的方法(尤其是第三方模塊),一個新的名叫apxs的支持程序(APache eXtenSion)被使用.它可以用來建立基於DSO的模塊,模塊位於Apache源碼樹以外。這個思路很簡單:當安裝Apache時,設置使安裝程序安裝Apache C 頭文件並且放置平台支持的編譯器和鏈接程序標志,用來建立DSO文件到apxs程序。通過這種方法,用戶可以使用apxs來編譯它的Apache模塊源代碼而不用Apache發布源代碼樹並且不用手動的添加平台支持的編譯程序和諒解程序的標志來獲得DSO支持。 為了放置編譯好的apache核心程序到一個DSO庫(僅僅在一些支持的平台上需要強迫鏈接程序輸出Apache核心的地址碼--一個DSO模塊化的先決條件)規定的SHARED_CORE必須能夠通過設置--允許-規定=SHARED_CORE設置(看頂層的INSTALL文件)或者通過改變Rule命令,在你的Configuration 文件裡規定SHARED_CORE=yes (看src/INSTALL文件).Apache核心代碼接著被放置到一個名叫libhttpd.so的DSO庫。因為靜態庫不能夠在所有的平台上被鏈接成為一個DSO,一個附加的名叫 libhttpd.ep的可執行程序被創建,這個程序不但包含這個靜態代碼而且有提供main()剩余部分 的功能.最後,httpd程序自己被用bootstrapping代碼替換,後者自動確定Unix調度程序能夠裝載並且開始libhttpd.ep,它通過提供LD_LIBRARY_PATH到libhttpd.so的方法實現. 最後我們再來說說Apache DSO模式支持的平台和它的優缺點--應該說目前DSO模式基本可運行在任何unix平台上--除了ultrix(因為它沒有動態(庫)裝載器即:No dlopen-style interface under this platform).它的優點是服務器包能夠在運行時更加靈活並且服務器包能夠簡單的用第三方模塊來擴展,那怕是在安裝之後。但同時DSO模式也有一些缺點如: 1、不能工作在所有平台下比如剛才說的不支持動態連接的ultrix。 2、Apache會在啟動時慢大約20%,因為要做一些前置作業,而地址碼的解決現在需要UNIX調度程序來做。 服務器在某些平台在執行時會慢5%,因為相對地址代碼(PIC)有時在不必要時也會需要重新編譯相對尋址,所以沒有絕對地址快.因此有時DSO不會提高速度。 3、因為DSO模塊會在個別平台上與別的基於DSO的庫(ld -lfoo)發生沖突(例如a.out-based 平台經常不支持這個功能,但是ELF-based平台支持)你不能為所有類型的模塊使用DSO機制(即不是所有的DSO模塊都能被加載)。 或者換一句話說,模塊作為DSO文件編譯是受限制,只能使用APACHE核心地址碼,APACHE核心使用的C庫(libc)和所有的別的動態和靜態的庫,或者靜態庫檔案(libfoo.a)包含的獨立的代碼。唯一使用別的代碼的辦法是或者確定APACHE核心自己早就包含自己的參考,通過dlopen()調用代碼自己,或者在建立APACHE時允許SHARED_CHAIN規則(當你的平台支持不用DSO庫鏈接DSO文件)。 在一些平台下(許多SVR4系統)在鏈接Apache httpd可執行程序時沒有辦法強迫鏈接程序輸出所有全部的DSO所用的地址.但是沒有可見的Apache核心地址碼就沒有標准的Apache模塊能夠作為DSO使用.唯一的辦法是使用SHARED_CORE特性,因為這種方法使全部的地址碼都被強制輸出。作為結果,Apache src/Configure script自動強制SHARED_CORE在這些平台上,當DSO特性被用在Configuration文件或者在configure命令行. 談了這麼多--都是原理性的東西--好象與我在序言中的實用性有些不符--其實DSO在Apache中是很重要的方面-也是比較高端的內容-要用好它需要對它的原理有所了解-另本文不足之處請大家指教批評-我會在下一篇DSO模式使用篇中說明如何使用及具體實現Apache的DSO模式。 -----注:參考文獻 Ralf S. Engelschall-- Apache 1.3 Dynamic Shared Object (DSO) Support 軟件屋Linux之家的中文翻譯。