apache DSO模式詳解原理

那么DSO究竟是什么?事實(shí)上DSO是Dynamic SharedObjects(動(dòng)態(tài)共享目標(biāo))的縮寫(xiě),它是現(xiàn)代Unix派生出來(lái)的操作系統(tǒng)都存在著的一種動(dòng)態(tài)連接機(jī)制。它提供了一種在運(yùn)行時(shí)將特殊格式的代碼,在程序運(yùn)行需要時(shí),將需要的部分從外存調(diào)入內(nèi)存執(zhí)行的方法。Apache在1.3以后的版本后開(kāi)始支持它。因?yàn)锳pache早就使用一個(gè)模塊概念來(lái)擴(kuò)展它的功能并且在內(nèi)部使用一個(gè)基于調(diào)度的列表來(lái)鏈接擴(kuò)展模塊到Apache核心模塊.所以, Apache早就注定要使用DSO來(lái)在運(yùn)行時(shí)加載它的模塊。
在mod_ssl和mod_rewrite的作者Ralf S. Engelschall(在我看來(lái)他是真正的Apache大師和主要開(kāi)發(fā)者之一,他的個(gè)人主頁(yè)上 的名篇“Apache 1.3 Dynamic Shared Object (DSO) Support”
中對(duì)DSO模式的原理有著比較詳盡的敘述(本文基本基于此)。


讓我們先來(lái)看一下Apache本身的程序結(jié)構(gòu):這是一個(gè)很復(fù)雜的四層結(jié)構(gòu)–每一層構(gòu)建在下一層之上。
第四層是用Apache模塊開(kāi)發(fā)的第三方庫(kù)–比如open ssl一般來(lái)說(shuō)在Apache的官方發(fā)行版中這層是空的,但是在實(shí)際的Apache結(jié)構(gòu)中這些庫(kù)構(gòu)成的層結(jié)構(gòu)肯定是存在的。
第三層是一些可選的附加功能模塊–如mod_ssl,mod_perl。這一層的每個(gè)模塊通常實(shí)現(xiàn)的是Apache的一個(gè)獨(dú)立的分離的功能而事實(shí)上這些模塊沒(méi)有一個(gè)是必須的,運(yùn)行一個(gè)最小的Apache不需要任何一個(gè)此層的模塊。
第二層是Apache的基本功能庫(kù)-這也是Apache的核心本質(zhì)層–這層包括Apache內(nèi)核,http_core(Apache的核心模塊),它們實(shí)現(xiàn)了基本HTTP功能(比如資源處理(通過(guò)文件描述符和內(nèi)存段等等),保持預(yù)生成(pre-forked)子進(jìn)程模型,監(jiān)聽(tīng)已配置的虛擬服務(wù)器的TCP/IP套接字,傳輸HTTP請(qǐng)求流到處理進(jìn)程,處理HTTP協(xié)議狀態(tài),讀寫(xiě)緩沖,此外還有附加的許多功能比如URL和MIME頭的解析及DSO的裝載等),也提供了Apache的應(yīng)用程序接口(API)(其實(shí)Apache的真正功能還是包含在內(nèi)部模塊中的,為了允許這些模塊完全控制Apache進(jìn)程,內(nèi)核必須提供API接口),這層也包括了一般性的可用代碼庫(kù)(libap)和實(shí)現(xiàn)正則表達(dá)式匹配的庫(kù)(libregex)還有就是一個(gè)小的操作系統(tǒng)的抽象庫(kù)(libos)。
最低層是與OS相關(guān)的平臺(tái)性應(yīng)用函數(shù),這些OS可以是不同現(xiàn)代UNIX的變種,win32,os/2,MacOS甚至只要是一個(gè)POSIX子系統(tǒng)。
在這個(gè)復(fù)雜的程序結(jié)構(gòu)中有趣的部分是—事實(shí)上第三層和第四層與第二層之間是松散的連接,而另一方面第三層的模塊間是互相依賴的–因這種結(jié)構(gòu)造成的顯著影響就是第三層和第四層的代碼不能靜態(tài)地連接到最低層平臺(tái)級(jí)的代碼上。因此DSO模式就成了解決它的一種手段。結(jié)合DSO功能,這個(gè)結(jié)構(gòu)就變得很靈活了,可以讓Apache內(nèi)核(從技術(shù)上說(shuō)應(yīng)該是mod_so模塊而不是內(nèi)核)在啟動(dòng)時(shí)(而不是安裝時(shí))裝載必要的部分以實(shí)現(xiàn)第三層和第四層的功能。
DSO在程序運(yùn)行時(shí)將需要的部分從外存調(diào)入內(nèi)存執(zhí)行存取通常會(huì)有兩種途徑:一種是ld由系統(tǒng)在程序開(kāi)始時(shí)自動(dòng)載入,這種載入可以由兩種途徑實(shí)現(xiàn):一種是自動(dòng)由系統(tǒng)在可執(zhí)行程序開(kāi)始時(shí)調(diào)用ld.so來(lái)執(zhí)行。另一種是手動(dòng),是通過(guò)在執(zhí)行程序里程序系統(tǒng)界面到Unix裝載程序通過(guò)系統(tǒng)調(diào)用tdlopen()/dlsym()來(lái)執(zhí)行的.
那么具體來(lái)說(shuō)Apache是怎么實(shí)現(xiàn)DSO功能的呢?DSO支持調(diào)用特別的Apache模塊是基于一個(gè)名叫mod_so.c的模塊,這個(gè)模塊必須靜態(tài)的編譯Apache的核心。這是除了http_core.c以外僅有的模塊不可以被放到DSO自己(bootstrapping!)。實(shí)際上所有的別的發(fā)布的Apache模塊都可以被放置到DSO,通過(guò)個(gè)別的通過(guò)設(shè)置被DSO建立允許–允許可能的-共享設(shè)置(看頂層的INSTALL文件)或者通過(guò)改變 AddModule命令在你的src/Configuration到SharedModule命令(看src/INSTALL文件)。
在編譯一個(gè)模塊到一個(gè)名字叫mod_foo.so的DSO以后,你能夠使用mod_so的LoadModule命令在你的httpd.conf文件里在服務(wù)程序開(kāi)始時(shí)或重新啟動(dòng)以后調(diào)用這個(gè)模塊。

為了簡(jiǎn)化這種為了Apache模塊而創(chuàng)建DSO文件的方法(尤其是第三方模塊),一個(gè)新的名叫apxs的支持程序(APacheeXtenSion)被使用.它可以用來(lái)建立基于DSO的模塊,模塊位于Apache源碼樹(shù)以外。這個(gè)思路很簡(jiǎn)單:當(dāng)安裝Apache時(shí),設(shè)置使安裝程序安裝Apache C頭文件并且放置平臺(tái)支持的編譯器和鏈接程序標(biāo)志,用來(lái)建立DSO文件到apxs程序。通過(guò)這種方法,用戶可以使用apxs來(lái)編譯它的Apache模塊源代碼而不用Apache發(fā)布源代碼樹(shù)并且不用手動(dòng)的添加平臺(tái)支持的編譯程序和諒解程序的標(biāo)志來(lái)獲得DSO支持。
為了放置編譯好的apache核心程序到一個(gè)DSO庫(kù)(僅僅在一些支持的平臺(tái)上需要強(qiáng)迫鏈接程序輸出Apache核心的地址碼–一個(gè)DSO模塊化的先決條件)規(guī)定的SHARED_CORE必須能夠通過(guò)設(shè)置–允許-規(guī)定=SHARED_CORE設(shè)置(看頂層的INSTALL文件)或者通過(guò)改變Rule命令,在你的Configuration 文件里規(guī)定SHARED_CORE=yes(看src/INSTALL文件).Apache核心代碼接著被放置到一個(gè)名叫l(wèi)ibhttpd.so的DSO庫(kù)。因?yàn)殪o態(tài)庫(kù)不能夠在所有的平臺(tái)上被鏈接成為一個(gè)DSO,一個(gè)附加的名叫 libhttpd.ep的可執(zhí)行程序被創(chuàng)建,這個(gè)程序不但包含這個(gè)靜態(tài)代碼而且有提供main()剩余部分的功能.最后,httpd程序自己被用bootstrapping代碼替換,后者自動(dòng)確定Unix調(diào)度程序能夠裝載并且開(kāi)始libhttpd.ep,它通過(guò)提供LD_LIBRARY_PATH到libhttpd.so的方法實(shí)現(xiàn).
最后我們?cè)賮?lái)說(shuō)說(shuō)ApacheDSO模式支持的平臺(tái)和它的優(yōu)缺點(diǎn)–應(yīng)該說(shuō)目前DSO模式基本可運(yùn)行在任何unix平臺(tái)上–除了ultrix(因?yàn)樗鼪](méi)有動(dòng)態(tài)(庫(kù))裝載器即:Nodlopen-style interface under thisplatform).它的優(yōu)點(diǎn)是服務(wù)器包能夠在運(yùn)行時(shí)更加靈活并且服務(wù)器包能夠簡(jiǎn)單的用第三方模塊來(lái)擴(kuò)展,那怕是在安裝之后。但同時(shí)DSO模式也有一些缺點(diǎn)如:

1、不能工作在所有平臺(tái)下比如剛才說(shuō)的不支持動(dòng)態(tài)連接的ultrix。
2、Apache會(huì)在啟動(dòng)時(shí)慢大約20%,因?yàn)橐鲆恍┣爸米鳂I(yè),而地址碼的解決現(xiàn)在需要UNIX調(diào)度程序來(lái)做。服務(wù)器在某些平臺(tái)在執(zhí)行時(shí)會(huì)慢5%,因?yàn)橄鄬?duì)地址代碼(PIC)有時(shí)在不必要時(shí)也會(huì)需要重新編譯相對(duì)尋址,所以沒(méi)有絕對(duì)地址快.因此有時(shí)DSO不會(huì)提高速度。
3、因?yàn)镈SO模塊會(huì)在個(gè)別平臺(tái)上與別的基于DSO的庫(kù)(ld -lfoo)發(fā)生沖突(例如a.out-based 平臺(tái)經(jīng)常不支持這個(gè)功能,但是ELF-based平臺(tái)支持)你不能為所有類型的模塊使用DSO機(jī)制(即不是所有的DSO模塊都能被加載)。或者換一句話說(shuō),模塊作為DSO文件編譯是受限制,只能使用APACHE核心地址碼,APACHE核心使用的C庫(kù)(libc)和所有的別的動(dòng)態(tài)和靜態(tài)的庫(kù),或者靜態(tài)庫(kù)檔案(libfoo.a)包含的獨(dú)立的代碼。唯一使用別的代碼的辦法是或者確定APACHE核心自己早就包含自己的參考,通過(guò)dlopen()調(diào)用代碼自己,或者在建立APACHE時(shí)允許 SHARED_CHAIN規(guī)則(當(dāng)你的平臺(tái)支持不用DSO庫(kù)鏈接DSO文件)。
在一些平臺(tái)下(許多SVR4系統(tǒng))在鏈接Apache httpd可執(zhí)行程序時(shí)沒(méi)有辦法強(qiáng)迫鏈接程序輸出所有全部的DSO所用的地址.但是沒(méi)有可見(jiàn)的Apache核心地址碼就沒(méi)有標(biāo)準(zhǔn)的Apache模塊能夠作為DSO使用.唯一的辦法是使用SHARED_CORE特性,因?yàn)檫@種方法使全部的地址碼都被強(qiáng)制輸出。作為結(jié)果,Apache src/Configure script自動(dòng)強(qiáng)制SHARED_CORE在這些平臺(tái)上,當(dāng)DSO特性被用在Configuration文件或者在configure命令行.