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