Apache HTTP Server是一个模块化程序,管理员可以在其中选择一组模块来选择要包含在服务器中的功能。模块将被编译为与主httpd
二进制文件分开存在的动态共享对象(DSO)。DSO模块可以在构建服务器时进行编译,也可以在以后使用Apache扩展工具(apxs
)进行编译和添加。
或者,可以在httpd
构建服务器时将模块静态编译为二进制文件。
本文档介绍了如何使用DSO模块以及其使用背后的理论。
相关模块 | 相关指令 |
---|---|
DSO对加载单个Apache httpd模块的支持基于名为的模块,该模块mod_so
必须静态编译到Apache httpd内核中。它是唯一
core
不能放入DSO本身的模块。实际上,所有其他分布式Apache httpd模块都将放入DSO。将模块编译成名为DSO
的文件后,mod_foo.so
可以在文件中使用mod_so
的LoadModule
指令在
httpd.conf
服务器启动或重新启动时加载该模块。
可以通过安装文档中
讨论configure
的--enable-mods-static
选项禁用针对单个模块的DSO构建
。
为了简化针对Apache httpd模块(尤其是第三方模块)的DSO文件的创建,提供了一个名为apxs
(APache eXtenSion)的支持程序。它可用于在Apache httpd源代码树之外构建基于DSO的模块。这个想法很简单:当安装Apache HTTP服务器configure
的
make install
程序安装在Apache httpd的C头文件并提出依赖于平台的编译器和连接器选项构建DSO文件到apxs
程序。这样,用户可以使用apxs
其编译Apache httpd模块源,而无需使用Apache httpd分发源树,而无需摆弄平台相关的编译器和链接器标志以获取DSO支持。
为了让您大致了解Apache HTTP Server 2.x的DSO功能,以下是一个简短的摘要:
例如,将分布式 Apache httpd模块
构建并安装mod_foo.c
到其自己的DSO中
mod_foo.so
:
$ ./configure --prefix=/path/to/install --enable-foo
$ make install
在启用所有模块的情况下配置Apache HTTP Server。服务器启动期间将仅加载基本集。您可以通过激活或停用中的LoadModule
指令
来更改已加载模块的集合httpd.conf
。
$ ./configure --enable-mods-shared=all
$ make install
有些模块仅对开发人员有用,无法构建。使用模块时设置全部。要构建包括开发人员模块在内的所有可用模块,请使用realall。另外,LoadModule
可以通过configure选项激活所有内置模块的
指令
--enable-load-all-modules
。
$ ./configure --enable-mods-shared=reallyall --enable-load-all-modules
$ make install
mod_foo.c
并将其安装到其自己的DSO中
:
mod_foo.so
apxs
$ cd /path/to/3rdparty
$ apxs -cia mod_foo.c
在所有情况下,一旦编译了共享模块,就必须使用LoadModule
in指令httpd.conf
告诉Apache httpd激活该模块。
有关更多详细信息,请参见apxs文档。
在现代Unix派生工具上,存在一种称为动态共享对象的动态链接/加载的机制,该机制提供了一种以特殊格式构建程序代码段的方法,以便在运行时将其加载到可执行程序的地址空间中。 。
这种加载通常可以通过两种方式完成:由ld.so
启动可执行程序时调用的系统程序自动完成,或通过编程系统接口从执行程序中手动通过系统调用与Unix加载器一起手动执行
dlopen()/dlsym()
。
在第一种方式中,DSO通常称为共享库或DSO库,并命名为
libfoo.so
或libfoo.so.1.2
。它们驻留在系统目录中(通常是/usr/lib
),并且通过指定-lfoo
linker命令在构建时建立到可执行程序的链接。此硬编码库引用到可执行程序文件,以便在起动时Unix加载器能够定位libfoo.so
在/usr/lib
,在路径经由像接头选项硬编码-R
或通过环境变量配置的路径
LD_LIBRARY_PATH
。然后,它解析DSO中可用的可执行程序中的任何符号(但尚未解析)。
DSO通常不引用可执行程序中的符号(因为它是通用代码的可重用库),因此无需进行进一步的解析。可执行程序无需使用DSO中的符号就可以自己做任何事情,因为完整的解析是由Unix加载器完成的。(实际上,要调用的代码
ld.so
是运行时启动代码的一部分,该代码链接到已绑定为非静态的每个可执行程序中)。动态加载公共库代码的优势显而易见:库代码只需要存储一次,就可以在系统库之类的系统库中libc.so
节省每个程序的磁盘空间。
第二种方法通常将DSO称为共享对象或DSO文件,并且可以使用任意扩展名进行命名(尽管规范名称为
foo.so
)。这些文件通常位于特定于程序的目录中,并且没有自动建立指向使用它们的可执行程序的链接。相反,可执行程序在运行时通过以下方式手动将DSO加载到其地址空间中:dlopen()
。此时,尚未完成来自DSO的可执行程序符号解析。但是,相反,Unix加载器会从可执行程序及其已加载的DSO库导出的符号集中自动解析DSO中的任何(尚未解析的)符号(尤其是来自普遍存在的所有符号libc.so
)。这样,DSO就可以像直接将它静态链接一样,获取可执行程序符号集的知识。
最后,为了利用DSO的API,可执行程序必须解析DSO中的特定符号,
dlsym()
以便以后在调度表等内部使用
。换句话说:可执行程序必须手动解析它需要的每个符号才能使用它。这种机制的优点是,在相关程序需要可选程序部件之前,不需要加载它们(因此不会浪费内存)。必要时,可以动态加载这些程序部分以扩展基本程序的功能。
尽管这种DSO机制听起来很简单,但这里至少有一个困难的步骤:使用DSO扩展程序时,从可执行程序中解析DSO的符号(第二种方法)。为什么?因为从可执行程序的符号集中“反向解析” DSO符号违反了库设计(库不了解其所使用的程序),并且在所有平台上都不可用,也没有标准化。实际上,可执行程序的全局符号通常不会重新导出,因此无法在DSO中使用。找到一种方法来强制链接器导出所有全局符号是使用DSO在运行时扩展程序时必须解决的主要问题。
共享库方法是典型的方法,因为它是DSO机制的设计目的,因此几乎用于操作系统提供的所有类型的库。
上述基于DSO的功能具有以下优点:
LoadModule
httpd.conf
配置指令而不是
configure
在构建时通过选项组装服务器进程
。例如,通过这种方式,仅安装一个Apache httpd 即可运行不同的服务器实例(标准和SSL版本,简约和动态版本[mod_perl,mod_php] 等)。apxs
对,您可以在Apache httpd源代码树之外工作,并且只需要一个apxs -i
命令后跟一个apachectl restart
即可将当前开发的模块的新版本引入正在运行的Apache HTTP Server。DSO具有以下缺点:
ld -lfoo
在所有平台上将DSO模块与其他基于DSO的库()链接(例如,基于a.out的平台通常不提供此功能,而基于ELF的平台却可以提供),因此您不能对所有类型的DSO使用DSO机制模块。换句话说,编译为DSO文件的模块只能使用Apache httpd核心,C库(libc
)和Apache httpd核心使用的所有其他动态或静态库或libfoo.a
包含以下内容的静态库归档文件()中的符号:位置无关代码。使用其他代码的唯一机会是确保httpd核心本身已经包含对其的引用,或者自己通过加载代码dlopen()
。