Apache之DSO小解

對DSO的理解還不是特別深刻,所以把自己查來的資料整理一下並想就此作一個總結。暫時先把資料堆到blog裏面了,有時間再整理總結。
 
一、以下源於《Apache HTTP Server Version 2.2 文檔》
 
動態共享對象(DSO)支持

Apache HTTP服務器是一個模塊化的軟件,管理員可以通過選擇服務器中包含的模塊進行功能增減。模塊可以在編譯時被靜態包含進httpd二進制文件,也可以編譯成獨立於httpd二進制文件的動態共享對象(DSO)DSO模塊可以與服務器一起編譯,也可以用Apache擴展工具(apxs)單獨編譯。

本文闡述如何使用DSO模塊及其工作原理。
 
實現
相關模塊

相關指令

mod_so

LoadModule

Apache對獨立模塊的DSO支持是建立在只能被靜態編譯進Apache核心的mod_so基礎之上的,這是core以外唯一不能作爲DSO存在的模塊,而其他所有已發佈的Apache模塊,都可以通過安裝文檔中闡述中的編譯選項 --enable-module=shared 被獨立地編譯成DSO並使之生效。一個被編譯爲mod_foo.soDSO模塊,可以在httpd.conf中使用mod_soLoadModule指令,在服務器啓動或重新啓動時被加載。

新提供的支持程序apxs(APache eXtenSion)可以在Apache源代碼樹之外編譯基於DSO的模塊,從而簡化了Apache DSO模塊的建立過程。其原理很簡單:安裝Apache時,configure make install 命令會安裝Apache C頭文件,並把依賴於特定平臺的編譯器和連接器參數傳給apxs程序,使用戶可以脫離Apache的發佈源代碼樹編譯其模塊源代碼,而不改變支持DSO的編譯器和連接器的參數。
 
用法概要
Apache2.0 DSO功能簡要說明:

編譯並安裝已發佈的Apache模塊,比如編譯mod_foo.cmod_foo.soDSO模塊:

$ ./configure --prefix=/path/to/install --enable-foo=shared
$ make install

編譯並安裝第三方模塊,比如編譯mod_foo.cmod_foo.soDSO模塊:

$ ./configure --add-module=module_type:/path/to/3rdparty/mod_foo.c --enable-foo=shared
$ make install

配置Apache以便以後安裝共享模塊:

$ ./configure --enable-so
$ make install

apxsApache源碼樹以外編譯並安裝第三方模塊,比如編譯mod_foo.cmod_foo.soDSO模塊:

$ cd /path/to/3rdparty
$ apxs -c mod_foo.c
$ apxs -i -a -n foo mod_foo.la

共享模塊編譯完畢後,必須在httpd.conf中用LoadModule指令使Apache啓用該模塊。
 
背景知識
現代的類Unix系統都有一種叫動態共享對象(DSO)的動態連接/加載的巧妙的機制,從而可以在運行時將編譯成特殊格式的代碼加載到一個可執行程序的地址空間。

加載的方法通常有兩種:其一是在可執行文件啓動時由系統程序ld.so自動加載;其二是在可執行程序中手動地通過Unix加載器的系統接口執行系統調用dlopen()/dlsym()進行加載。

按第一種方法,DSO通常被稱爲共享庫(shared libraries)或者DSO(DSO libraries),使用libfoo.solibfoo.so.1.2的文件名,存儲在系統目錄中(通常是/usr/lib),並在編譯安裝時使用連接器參數 -lfoo 建立了指向可執行程序的連接。通過設置連接器參數 -R 或者環境變量LD_LIBRARY_PATH ,庫中硬編碼了可執行文件的路徑,使Unix加載器能夠在可執行程序啓動時定位到位於/usr/lib目錄中的libfoo.so ,以解析可執行文件中尚未解析的位於DSO中的符號。

通常,DSO不會引用可執行文件中的符號(因爲它是通用代碼的可重用庫),也不會有後繼的解析動作。可執行文件無須自己作任何動作以使用DSO中的符號,而完全由Unix加載器×××(事實上,調用ld.so的代碼是被連入每個可執行文件的非靜態運行時啓動代碼的一部分)。動態加載公共庫代碼的優點是明顯的:只需要在系統庫libc.so中存儲一次庫代碼,從而爲每個程序節省了磁盤存儲空間。

按第二種方法,DSO通常被稱爲共享對象(shared objects)DSO文件(DSO files),可以使用任何文件名(但是規範的名稱是foo.so),被存儲在程序特定的目錄中,也不會自動建立指向其所用的可執行文件的連接,而由可執行文件在運行時自己調用dlopen()來加載DSO到其地址空間,同時也不會進行爲可執行文件解析DSO中符號的操作。Unix加載器會根據可執行程序的輸出符號表和已經加載的DSO庫自動解析DSO中尚未解析的符號(尤其是無所不在的libc.so中的符號),如此DSO就獲得了可執行程序的符號信息,就好象是被靜態連接一樣。

最後,爲了利用DSO API的優點,可執行程序必須用dlsym()解析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的功能有如下優點:

由於服務器包的裝配工作可以在運行時使用httpd.conf中的配置命令LoadModule來進行,而不是在編譯中使用編譯選項來進行,因此顯得更靈活。比如,只需要安裝一個Apache,就可以運行多個不同的服務器實例(如標準&SSL版本,濃縮&功能加強版本[mod_perlPHP])

服務器可以在安裝後使用第三方模塊被輕易地擴展。這至少對廠商發行包的維護者有巨大的好處,他可以建立一個Apache核心包,而爲諸如PHPmod_perlmod_fastcgi等擴展另建附加的包。

更簡單的Apache模塊原型。使用DSO配合apxs,可以脫離Apache源代碼樹,僅需要一個 apxs -i 和一個 apachectl restart 命令,就可以把剛開發的新模塊納入到運行中的Apache服務器。

DSO有如下缺點:

由於並不是所有操作系統都支持動態加載代碼到一個程序的地址空間,因此DSO機制並不能用於所有平臺。

由於Unix加載器必須進行符號解析,服務器的啓動會慢20%左右。

在某些平臺上,位置獨立代碼(positon independent code[PIC])需要複雜的彙編語言技巧來實現相對尋址,而絕對尋址則不需要,因此服務器在運行時會慢5%左右。

由於DSO模塊不能在所有平臺上被其他基於DSO的庫所連接(ld -lfoo),比如,基於a.out的平臺通常不提供此功能,而基於ELF的平臺則提供,因此DSO機制並不能被用於所有類型的模塊。或者可以這樣說,編譯爲DSO文件的模塊只能使用由Apache核心、C(libc)Apache核心所用的所有其他動態或靜態的庫、含有獨立位置代碼的靜態庫(libfoo.a)所提供的符號。而要使用其他代碼,就只能確保Apache核心本身包含對此代碼的引用,或者自己用dlopen()來加載此代碼。

 

 

二、以下源於《Apache系統管理實用技巧集(2)》

 

在網上的中文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中是很重要的方面-也是比較高端的內容-要用好它需要對它的原理有所瞭解-另本文不足之處請大家指教批評。
-----注:參考文獻
Ralf S. Engelschall--
Apache 1.3 Dynamic Shared Object (DSO) Support 軟件屋linux之家的中文翻譯。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章