Apache HTTP Server 版本 2.4
Apache HTTP Server 是一个模块化程序,管理员可以通过选择一组模块来决定在服务器中包含哪些功能。模块将被编译为动态共享对象 (DSO),它们独立于主 httpd
二进制文件存在。DSO 模块可以在服务器构建时编译,也可以在稍后使用 Apache 扩展工具 (apxs
) 编译并添加。
或者,模块可以在服务器构建时静态编译到 httpd
二进制文件中。
本文档介绍了如何使用 DSO 模块以及它们背后的理论。
相关模块 | 相关指令 |
---|---|
加载单个 Apache httpd 模块的 DSO 支持基于名为 mod_so
的模块,该模块必须静态编译到 Apache httpd 核心。除了 core
之外,它是唯一不能放入 DSO 本身的模块。实际上,所有其他分发的 Apache httpd 模块都将被放置到 DSO 中。在将模块编译到名为 mod_foo.so
的 DSO 后,您可以在 httpd.conf
文件中使用 mod_so
的 LoadModule
指令在服务器启动或重启时加载此模块。
可以通过 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 支持调整平台相关的编译器和链接器标志。
为了让您了解 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。服务器启动时只会加载基本集。您可以通过激活或停用 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 文档。
在现代 Unix 派生系统中,存在一种称为动态共享对象 (DSO) 的动态链接/加载机制,它提供了一种方法来构建特殊格式的程序代码片段,以便在运行时将其加载到可执行程序的地址空间中。
这种加载通常可以通过两种方式完成:由名为 ld.so
的系统程序在可执行程序启动时自动完成,或者通过系统调用 dlopen()/dlsym()
手动从正在执行的程序中通过对 Unix 加载程序的编程系统接口完成。
在第一种方式中,DSO 通常被称为共享库或DSO 库,并命名为 libfoo.so
或 libfoo.so.1.2
。它们驻留在系统目录(通常为 /usr/lib
)中,并且通过在链接器命令中指定 -lfoo
来建立与可执行程序的链接。这将库引用硬编码到可执行程序文件中,以便在启动时,Unix 加载程序能够在 /usr/lib
中找到 libfoo.so
,在通过链接器选项(如 -R
)硬编码的路径中找到,或在通过环境变量 LD_LIBRARY_PATH
配置的路径中找到。然后,它会解析可执行程序中所有(尚未解析的)符号,这些符号在 DSO 中可用。
可执行程序中的符号通常不会被 DSO 引用(因为它是一个通用的代码库),因此无需进行进一步的解析。可执行程序无需自行执行任何操作即可使用 DSO 中的符号,因为所有解析都由 Unix 加载程序完成。(实际上,调用 ld.so
的代码是运行时启动代码的一部分,该代码链接到每个已非静态绑定到可执行程序中。)动态加载通用库代码的优势显而易见:库代码只需要存储一次,在一个系统库中,例如 libc.so
,为每个程序节省磁盘空间。
在第二种方式中,DSO 通常被称为共享对象或DSO 文件,并且可以使用任意扩展名命名(尽管规范名称为 foo.so
)。这些文件通常保留在程序特定的目录中,并且没有与使用它们的执行程序建立自动链接。相反,可执行程序在运行时通过 dlopen()
手动将 DSO 加载到其地址空间中。此时,不会对可执行程序解析 DSO 中的任何符号。但相反,Unix 加载程序会自动解析 DSO 中所有(尚未解析的)符号,这些符号来自可执行程序及其已加载的 DSO 库(尤其是来自无处不在的 libc.so
的所有符号)导出的符号集。这样,DSO 就可以了解可执行程序的符号集,就好像它最初是与可执行程序静态链接的一样。
最后,为了利用 DSO 的 API,可执行程序必须通过 dlsym()
解析 DSO 中的特定符号,以便在以后的调度表等中使用。换句话说:可执行程序必须手动解析它需要使用的每个符号。这种机制的优势在于,可选的程序部分无需加载(因此不会占用内存),直到它们被相关程序需要。需要时,这些程序部分可以动态加载以扩展基本程序的功能。
虽然这种 DSO 机制听起来很简单,但至少有一个困难的步骤:在使用 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
)(例如,基于 a.out 的平台通常不提供此功能,而基于 ELF 的平台则提供),因此您无法将 DSO 机制用于所有类型的模块。或者换句话说,编译为 DSO 文件的模块仅限于使用来自 Apache httpd 核心、C 库 (libc
) 以及 Apache httpd 核心使用的所有其他动态或静态库,或来自包含位置无关代码的静态库档案 (libfoo.a
) 的符号。使用其他代码的唯一机会是确保 httpd 核心本身已经包含对它的引用,或者通过 dlopen()
自己加载代码。