动态共享对象(DSO)支持

Apache HTTP Server 是一个模块化程序,Management 员可以在其中选择一组模块来选择要包含在服务器中的功能。模块将被编译为与主要httpd二进制文件分开存在的动态共享对象(DSO)。 DSO 模块可以在构建服务器时进行编译,也可以在以后使用 Apache 扩展工具(apxs)进行编译和添加。

或者,可以在构建服务器时将模块静态编译为httpd二进制文件。

本文档介绍了如何使用 DSO 模块以及其使用背后的理论。

Implementation

Related ModulesRelated Directives
mod_soLoadModule

DSO 对加载单个 Apache httpd 模块的支持基于名为mod_so的模块,该模块必须静态编译到 Apache httpd 内核中。它是core之外唯一无法放入 DSO 的模块。实际上,所有其他分布式 Apache httpd 模块都将放入 DSO。将模块编译成名为mod_foo.so的 DSO 之后,可以在httpd.conf文件中使用mod_soLoadModule指令在服务器启动或重新启动时加载此模块。

install documentation中所述,可以通过configure--enable-mods-static选项禁用针对单个模块的 DSO 构建。

为了简化针对 Apache httpd 模块(尤其是第三方模块)的 DSO 文件的创建,提供了名为apxs(APache eXtenSion)的支持程序。它可以用于在 Apache httpd 源代码树之外构建基于 DSO 的模块。这个想法很简单:安装 Apache HTTP Server 时,configuremake 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到其自己的 DSO mod_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.solibfoo.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()加载代码。