本文檔介紹了如何使用DSO模塊及其使用背后的理論。
DSO支持的實(shí)現(xiàn)
DSO對加載單個(gè)Apache httpd模塊的支持基于名為mod_so
的模塊,該模塊必須靜態(tài)編譯到Apache httpd核心中。除了核心之外,它是唯一不能放入DSO本身的模塊。實(shí)際上,所有其他分布式Apache httpd模塊將被放入DSO中。將模塊編譯為名為mod_foo.so
的DSO后,可以在httpd.conf
文件中使用mod_so
的LoadModule
指令在服務(wù)器啟動(dòng)或重新啟動(dòng)時(shí)加載此模塊。
可以通過configure
命令的--enable-mods-static
選項(xiàng)禁用單個(gè)模塊的DSO構(gòu)建,如安裝文檔中所述。
為了簡化Apache httpd模塊(尤其是第三方模塊)的DSO文件的創(chuàng)建,可以使用名為apxs(APache eXtenSion)的支持程序。它可用于在Apache httpd源樹之外構(gòu)建基于DSO的模塊。這個(gè)想法很簡單:在安裝Apache HTTP Server時(shí),configure的make install
過程會(huì)安裝Apache httpd C頭文件,并將用于構(gòu)建DSO文件的平臺(tái)相關(guān)編譯器和鏈接器標(biāo)志放入apxs程序中。這樣,用戶可以使用apxs編譯他的Apache httpd模塊源代碼,而無需Apache httpd分發(fā)源代碼樹,也無需為DSO支持提供依賴于平臺(tái)的編譯器和鏈接器標(biāo)志。
使用摘要
為了概述Apache HTTP Server 2.x的DSO功能,這里有一個(gè)簡短的摘要(步驟):
第1步 - 構(gòu)建并將分布式Apache httpd模塊(例如mod_foo.c
)安裝到自己的DSO mod_foo.so
中:
$ ./configure --prefix=/path/to/install --enable-foo $ make install
Shell
第2步 - 配置Apache HTTP Server并啟用所有模塊。服務(wù)器啟動(dòng)期間僅加載基本集??梢酝ㄟ^激活或取消激活httpd.conf
中的LoadModule指令來更改已加載模塊的集合。
$ ./configure --enable-mods-shared=all $ make install
Shell
第3步 - 有些模塊僅對開發(fā)人員有用,不會(huì)構(gòu)建。使用模塊時(shí)全部設(shè)置。要構(gòu)建所有可用的模塊,包括開發(fā)人員模塊都可使用。此外,可以通過configure
的--enable-load-all-modules
選項(xiàng)激活所有構(gòu)建模塊的LoadModule指令。
$ ./configure --enable-mods-shared=reallyall --enable-load-all-modules $ make install
Shell
使用apxs在Apache httpd源代碼樹之外構(gòu)建并安裝第三方Apache httpd模塊(例如mod_foo.c
)到其自己的DSO mod_foo.so
中:
$ cd /path/to/3rdparty $ apxs -cia mod_foo.c
Shell
在所有情況下,一旦編譯了共享模塊,就必須在httpd.conf
中使用LoadModule指令來告訴Apache httpd激活模塊。
背后機(jī)制
在現(xiàn)代Unix衍生產(chǎn)品中,存在一種稱為動(dòng)態(tài)共享對象(DSO)的動(dòng)態(tài)鏈接/加載機(jī)制,它提供了一種以特殊格式構(gòu)建程序代碼的方法,以便在運(yùn)行時(shí)將其加載到可執(zhí)行程序的地址空間中。
這種加載通??梢酝ㄟ^兩種方式完成:當(dāng)一個(gè)可執(zhí)行程序啟動(dòng)時(shí),通過一個(gè)名為ld.so
的系統(tǒng)程序自動(dòng)完成,或者通過系統(tǒng)調(diào)用dlopen()
/dlsym()
通過編程系統(tǒng)接口從Unix加載器手動(dòng)執(zhí)行程序。
在第一種方式中,DSO通常稱為共享庫或DSO庫,并命名為libfoo.so
或libfoo.so.1.2
。它們駐留在系統(tǒng)目錄(通常是/usr/lib
)中,并且通過在鏈接器命令中指定-lfoo
,在構(gòu)建時(shí)建立可執(zhí)行程序的鏈接。這個(gè)硬編碼庫引用了可執(zhí)行程序文件,因此在啟動(dòng)時(shí),Unix加載器能夠在/usr/lib
中找到libfoo.so
,在通過鏈接器選項(xiàng)(如-R)進(jìn)行硬編碼的路徑中,或者在通過環(huán)境變量LD_LIBRARY_PATH
。然后它解析可執(zhí)行程序中可用于DSO的任何(尚未解決的)符號。
可執(zhí)行程序中的符號通常不會(huì)被DSO引用(因?yàn)樗强芍赜玫耐ㄓ么a庫),因此不需要進(jìn)一步解析??蓤?zhí)行程序不需要自己做任何事情來使用DSO中的符號,因?yàn)橥暾慕馕鍪怯蒛nix加載器完成的。實(shí)際上,調(diào)用ld.so
的代碼是運(yùn)行時(shí)啟動(dòng)代碼的一部分,該代碼鏈接到已經(jīng)綁定為非靜態(tài)的每個(gè)可執(zhí)行程序。動(dòng)態(tài)加載公共庫代碼的優(yōu)勢顯而易見:庫代碼只需要存儲(chǔ)一次,就像libc.so
這樣的系統(tǒng)庫,為每個(gè)程序節(jié)省磁盤空間。
在第二種方式中,DSO通常稱為共享對象或DSO文件,并且可以使用任意擴(kuò)展名命名(盡管規(guī)范名稱為foo.so
)。這些文件通常保留在特定于程序的目錄中,并且沒有自動(dòng)建立的鏈接指向使用它們的可執(zhí)行程序。相反,可執(zhí)行程序通過dlopen()
手動(dòng)將DSO在運(yùn)行時(shí)加載到其地址空間。此時(shí),不會(huì)從DSO解析可執(zhí)行程序的符號。但是,Unix加載程序會(huì)自動(dòng)解析DSO中可執(zhí)行程序?qū)С龅姆柤捌湟鸭虞d的DSO庫(尤其是來自無處不在的libc.so的所有符號)中的任何(尚未解析的)符號。通過這種方式,DSO可以了解可執(zhí)行程序的符號集,就好像它首先與它靜態(tài)鏈接一樣。
最后,為了利用DSO的API,可執(zhí)行程序必須通過dlsym()解析DSO中的特定符號,以便以后在調(diào)度表等內(nèi)部使用。換句話說:可執(zhí)行程序必須手動(dòng)解析它需要的每個(gè)符號才能使用它。這種機(jī)制的優(yōu)點(diǎn)在于,在所討論的程序需要它們之前,不需要加載可選的程序部分(因此不需要花費(fèi)內(nèi)存)。必要時(shí),可以動(dòng)態(tài)加載這些程序部分以擴(kuò)展基本程序的功能。
盡管這種DSO機(jī)制聽起來很簡單,但至少有一個(gè)困難的步驟:當(dāng)使用DSO擴(kuò)展程序時(shí),從DSO的可執(zhí)行程序中解析符號(第二種方式)。為什么?因?yàn)閬碜钥蓤?zhí)行程序符號集的“反向解析”DSO符號是針對庫設(shè)計(jì)的(庫不知道它所使用的程序),并且既不是在所有平臺(tái)上都可用,也不是標(biāo)準(zhǔn)化的。實(shí)際上,可執(zhí)行程序的全局符號通常不會(huì)重新導(dǎo)出,因此無法在DSO中使用。找到一種強(qiáng)制鏈接器導(dǎo)出所有全局符號的方法是使用DSO在運(yùn)行時(shí)擴(kuò)展程序時(shí)必須解決的主要問題。
共享庫方法是典型的方法,因?yàn)樗荄SO機(jī)制的設(shè)計(jì)方法,因此它幾乎用于操作系統(tǒng)提供的所有類型的庫。
DSO優(yōu)點(diǎn)和缺點(diǎn)
基于DSO的功能具有以下優(yōu)點(diǎn):
- 服務(wù)器包在運(yùn)行時(shí)更靈活,因?yàn)榉?wù)器進(jìn)程可以在運(yùn)行時(shí)通過
httpd.conf
配置 LoadModule 指令而不是在構(gòu)建時(shí)配置選項(xiàng)進(jìn)行組裝。例如,通過這種方式,只需一個(gè)Apache httpd安裝即可運(yùn)行不同的服務(wù)器實(shí)例(標(biāo)準(zhǔn)版和SSL版,簡約版和動(dòng)態(tài)版[mod_perl,mod_php]等)。 - 即使在安裝后,也可以使用第三方模塊輕松擴(kuò)展服務(wù)器包。這對于供應(yīng)商軟件包維護(hù)者來說是一個(gè)很大的好處,他們可以創(chuàng)建Apache httpd核心軟件包以及包含PHP,
mod_perl
,mod_security
等擴(kuò)展的其他軟件包。 - 更簡單的Apache httpd模塊原型設(shè)計(jì),因?yàn)槭褂肈SO/apxs對,可以在Apache httpd源樹之外工作,只需要
apxs -i
命令,然后重啟apachectl
,即可將當(dāng)前開發(fā)的模塊的新版本帶入正在運(yùn)行的Apache HTTP服務(wù)器。
DSO具有以下缺點(diǎn):
- 由于Unix加載器現(xiàn)在必須執(zhí)行的符號解決開銷,服務(wù)器在啟動(dòng)時(shí)的速度大約慢20%。
- 在某些平臺(tái)下,服務(wù)器在執(zhí)行時(shí)的速度大約慢5%,因?yàn)槲恢脽o關(guān)代碼(PIC)有時(shí)需要復(fù)雜的匯編器技巧來進(jìn)行相對尋址,這不一定和絕對尋址一樣快。
- 由于DSO模塊無法在所有平臺(tái)上與其他基于DSO的庫(
ld -lfoo
)鏈接(例如,基于a.out的平臺(tái)通常不提供此功能,而基于ELF的平臺(tái)),因此無法使用DSO機(jī)制所有類型的模塊?;蛘邠Q句話說,編譯為DSO文件的模塊僅限于使用來自Apache httpd核心,C庫(libc)以及Apache httpd核心使用的所有其他動(dòng)態(tài)或靜態(tài)庫或靜態(tài)庫歸檔(libfoo.a
)包含與位置無關(guān)的代碼。使用其他代碼的唯一機(jī)會(huì)是確保httpd核心本身已包含對它的引用或通過dlopen()
自己加載代碼。
更多建議: