閱讀226 返回首頁    go 王者榮耀


apache, DSO原理

Apache HTTP服務器是一個模塊化的軟件,使管理者可以選擇核心中包含的模塊以裁剪功能。可以在編譯時選擇被靜態包含進httpd二進製映象的模塊,也可以編譯成獨立於主httpd二進製映象的動態共享對象DSODSO模塊可以在編譯服務器之後編譯,也可以用Apache擴展工具(apxs)編譯並增加。

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

工作原理

DSODynamic Shared Objects(動態共享目標)的縮寫,它是現代Unix派生出來的操作係統都存在著的一種動態連接機製。它提供了一種在運行時將特殊格式的代碼,在程序運行需要時,將需要的部分從外存調入內存執行的方法。Apache1.3以後的版本後開始支持它。因為Apache早就使用一個模塊概念來擴展它的功能並且在內部使用一個基於調度的列表來鏈接擴展模塊到Apache核心模塊.所以,Apache早就注定要使用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協議狀態,讀寫緩衝,此外還有附加的許多功能比如URLMIME頭的解析及DSO的裝載等),也提供了Apache的應用程序接口(API)(其實Apache的真正功能還是包含在內部模塊中的,為了允許這些模塊完全控製Apache進程,內核必須提供API接口),這層也包括了一般性的可用代碼庫(libap)和實現正則表達式匹配的庫(libregex)還有就是一個小的操作係統的抽象庫(libos)

最低層是與OS相關的平台性應用函數,這些OS可以是不同現代UNIX的變種,win32,os/2,MacOS甚至隻要是一個POSIX子係統。

apache模塊功能分層

模塊功能分層

在這個複雜的程序結構中有趣的部分是---事實上第三層和第四層與第二層之間是鬆散的連接,而另一方麵第三層的模塊間是互相依賴的--因這種結構造成的顯著影響就是第三層和第四層的代碼不能靜態地連接到最低層平台級的代碼上。因此DSO模式就成了解決它的一種手段。結合DSO功能,這個結構就變得很靈活了,可以讓Apache內核(從技術上說應該是mod_so模塊而不是內核)在啟動時(而不是安裝時)裝載必要的部分以實現第三層和第四層的功能。

現代類Unix的係統都有一種叫動態共享對象(DSO)的動態連接/加載的巧妙的機製,從而可以在運行時刻,將編譯成特殊格式的代碼加載到一個可執行程序的地址空間。

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

按第一種方法,DSO通常被稱為共享庫(shared libraries)或者DSO(DSO libraries),使用libfoo.so或者libfoo.so.1.2的文件名,被存儲在係統目錄中(通常是/usr/lib),並在編譯安裝時,使用連接器參數-lfoo建立了指向可執行程序的連接。通過設置連接器參數-R或者環境變量LD_LIBRARY_PATH,庫中硬編碼了可執行文件的路徑,使Unix加載器能夠定位到位於/usr/liblibfoo.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來進行,而不是在編譯中使用configure來進行,因此顯得更靈活。比如:隻需要安裝一個Apache,就可以運行多個不同的服務器實例(如標準&SSL版本,濃縮的&功能加強版本[mod_perl, PHP3]等等)
  • 服務器包可以在安裝後使用第三方模塊被輕易地擴展。這至少對廠商發行包的維護者有巨大的好處,他可以建立一個Apache核心包,而為諸如PHP3, mod_perl, mod_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對獨立模塊的DSO支持是建立在被靜態編譯進Apache核心的mod_so模塊基礎上的,這是core以外唯一不能作為DSO存在的模塊,而其他所有已發布的Apache模塊,都可以通過安裝文檔中闡述的配置選項--enable-module=shared,被獨立地編譯成DSO並使之生效。一個被編譯為mod_foo.soDSO模塊,可以在httpd.conf中使用mod_soLoadModule指令,在服務器啟動或重新啟動時被加載。

用命令行參數-l可以查看已經編譯到服務器中的模塊。

新提供的支持程序apxs(APache eXtenSion)可以在Apache源代碼樹以外編譯基於DSO的模塊,從而簡化Apache DSO模塊的建立過程。其原理很簡單:安裝Apache時,配置命令make install會安裝Apache C頭文件,並把依賴於平台的編譯器和連接器參數傳給apxs程序,使用戶可以脫離Apache的發布源代碼樹編譯其模塊源代碼,而不改變支持DSO的編譯器和連接器的參數。

Apache 2.0DSO功能簡要說明:

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

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

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

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

  1. 配置Apache以便共享後續安裝的模塊:

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

  1. apxsApache源代碼樹以外編譯並安裝第三方Apache模塊,比如編譯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激活該模塊。

參考資料:

Ralf S. Engelschall--     Apache 1.3 Dynamic Shared Object (DSO) Support

Apache2.0 文檔-- 動態共享對象

最後更新:2017-04-02 00:06:25

  上一篇:go [圖文]曆屆奧斯卡影帝(上)
  下一篇:go 一道有趣的C#考試題目