# 动态共享对象(DSO)支持
Apache HTTP服务器是一个模块化的软件,管理员可以通过选择服务器中包含的模块进行功能增减。模块可以在编译时被静态包含进`httpd`二进制文件,也可以编译成独立于`httpd`二进制文件的动态共享对象(DSO)。DSO模块可以与服务器一起编译,也可以用Apache扩展工具(`apxs`)单独编译。
本文阐述如何使用DSO模块及其工作原理。
## 实现
相关模块
* `mod_so`
相关指令
* `LoadModule`
Apache对独立模块的DSO支持是建立在只能被静态编译进Apache核心的`mod_so`基础之上的,这是`core`以外唯一不能作为DSO存在的模块,而其他所有已发布的Apache模块,都可以通过[安装文档](#calibre_link-249)中阐述中的`编译选项` `--enable-_module_=shared` 被独立地编译成DSO并使之生效。一个被编译为`mod_foo.so`的DSO模块,可以在`httpd.conf`中使用`mod_so`的`LoadModule`指令,在服务器启动或重新启动时被加载。
新提供的支持程序`apxs`(<dfn class="calibre27">APache eXtenSion</dfn>)可以在Apache源代码树_之外_编译基于DSO的模块,从而简化了Apache DSO模块的建立过程。其原理很简单:安装Apache时,`configure`的 `make install` 命令会安装Apache C头文件,并把依赖于特定平台的编译器和连接器参数传给`apxs`程序,使用户可以脱离Apache的发布源代码树编译其模块源代码,而不改变支持DSO的编译器和连接器的参数。
## 用法概要
Apache2.0 的DSO功能简要说明:
1. 编译并安装_已发布_的Apache模块,比如编译`mod_foo.c`为`mod_foo.so`的DSO模块:
```
$ ./configure --prefix=/path/to/install --enable-foo=shared
$ make install
```
2. 编译并安装_第三方_模块,比如编译`mod_foo.c`为`mod_foo.so`的DSO模块:
```
$ ./configure --add-module=module_type:/path/to/3rdparty/mod_foo.c --enable-foo=shared
$ make install
```
3. 配置Apache以便_以后安装_共享模块:
```
$ ./configure --enable-so
$ make install
```
4. 用`apxs`在Apache源码树_以外_编译并安装_第三方_模块,比如编译`mod_foo.c`为`mod_foo.so`的DSO模块:
```
$ cd /path/to/3rdparty
$ apxs -c mod_foo.c
$ apxs -i -a -n foo mod_foo.la
```
共享模块编译完毕后,必须在`httpd.conf`中用`LoadModule`指令使Apache启用该模块。
## 背景知识
现代的类Unix系统都有一种叫_动态共享对象_(DSO)的动态连接/加载的巧妙的机制,从而可以在运行时将编译成特殊格式的代码加载到一个可执行程序的地址空间。
加载的方法通常有两种:其一是在可执行文件启动时由系统程序`ld.so`自动加载;其二是在可执行程序中手动地通过Unix加载器的系统接口执行系统调用`dlopen()/dlsym()`进行加载。
按第一种方法,DSO通常被称为_共享库_(shared libraries)或者_DSO库_(DSO libraries),使用`libfoo.so`或`libfoo.so.1.2`的文件名,存储在系统目录中(通常是`/usr/lib`),并在编译安装时使用连接器参数 `-lfoo` 建立了指向可执行程序的连接。通过设置连接器参数 `-R` 或者环境变量`LD_LIBRARY_PATH` ,库中硬编码了可执行文件的路径,使Unix加载器能够在可执行程序启动时定位到位于`/usr/lib`目录中的`libfoo.so` ,以解析可执行文件中尚未解析的位于DSO中的符号。
通常,DSO不会引用可执行文件中的符号(因为它是通用代码的可重用库),也不会有后继的解析动作。可执行文件无须自己作任何动作以使用DSO中的符号,而完全由Unix加载器代办(事实上,调用`ld.so`的代码是被连入每个可执行文件的非静态运行时启动代码的一部分)。动态加载公共库代码的优点是明显的:只需要在系统库`libc.so`中存储一次库代码,从而为每个程序节省了磁盘存储空间。
按第二种方法,DSO通常被称为_共享对象_(shared objects)或_DSO文件_(DSO files),可以使用任何文件名(但是规范的名称是`foo.so`),被存储在程序特定的目录中,也不会自动建立指向其所用的可执行文件的连接,而由可执行文件在运行时自己调用`dlopen()`来加载DSO到其地址空间,同时也不会进行为可执行文件解析DSO中符号的操作。Unix加载器会根据可执行程序的输出符号表和已经加载的DSO库自动解析DSO中尚未解析的符号(尤其是无所不在的`libc.so`中的符号),如此DSO就获得了可执行程序的符号信息,就好象是被静态连接一样。
最后,为了利用DSO API的优点,可执行程序必须用`dlsym()`解析DSO中的符号,以备稍后在诸如指派表_等等_中使用。也就是说,可执行程序必须自己解析其所需的符号。这种机制的优点是允许不加载可选的程序部件,直到程序需要的时候才被动态地加载(也就不需要内存开销),以扩展程序的功能。
虽然这种DSO机制看似很直接,但至少有一个难点,就是在用DSO扩展程序功能(第二种方法)时为DSO对可执行程序中符号的进行解析,这是因为,"反向解析"可执行程序中的DSO符号在所有标准平台上与库的设计都是矛盾的(库不会知道什么程序会使用它)。实际应用中,可执行文件中的全局符号通常不是重输出的,因此不能为DSO所用。所以在运行时用DSO来扩展程序功能,就必须找到强制连接器输出所有全局符号的方法。
共享库是一种典型的解决方法,因为它符合DSO机制,而且为操作系统所提供的几乎所有类型的库所使用。另一方面,使用共享对象并不是许多程序为扩展其功能所采用的方法。
截止到1998年,只有少数的软件包使用DSO机制在运行时扩展其功能,诸如 Perl 5(通过其XS机制和DynaLoader模块),Netscape Server等。从1.3版本开始,Apache也加入此列,因为Apache已经用了基于指派表(dispatch-list-based)的方法来连接外部模块到Apache的核心。所以Apache也就当然地在运行时用DSO来加载其模块。
## 优点和缺点
上述基于DSO的功能有如下优点:
* 由于服务器包的装配工作可以在运行时使用`httpd.conf`中的配置命令`LoadModule`来进行,而不是在编译中使用`编译选项`来进行,因此显得更灵活。比如,只需要安装一个Apache,就可以运行多个不同的服务器实例(如标准&SSL版本,浓缩&功能加强版本[mod_perl、PHP])。
* 服务器可以在安装后使用第三方模块被轻易地扩展。这至少对厂商发行包的维护者有巨大的好处,他可以建立一个Apache核心包,而为诸如PHP、mod_perl、mod_fastcgi等扩展另建附加的包。
* 更简单的Apache模块原型。使用DSO配合`apxs`,可以脱离Apache源代码树,仅需要一个 `apxs -i` 和一个 `apachectl restart` 命令,就可以把刚开发的新模块纳入到运行中的Apache服务器。
DSO有如下缺点:
* 由于并不是所有操作系统都支持动态加载代码到一个程序的地址空间,因此DSO机制并不能用于所有平台。
* 由于Unix加载器必须进行符号解析,服务器的启动会慢20%左右。
* 在某些平台上,位置独立代码(positon independent code[PIC])需要复杂的汇编语言技巧来实现相对寻址,而绝对寻址则不需要,因此服务器在运行时会慢5%左右。
* 由于DSO模块不能在所有平台上被其他基于DSO的库所连接(`ld -lfoo`),比如,基于a.out的平台通常不提供此功能,而基于ELF的平台则提供,因此DSO机制并不能被用于所有类型的模块。或者可以这样说,编译为DSO文件的模块只能使用由Apache核心、C库(`libc`)和Apache核心所用的所有其他动态或静态的库、含有独立位置代码的静态库(`libfoo.a`)所提供的符号。而要使用其他代码,就只能确保Apache核心本身包含对此代码的引用,或者自己用`dlopen()`来加载此代码。
- Apache HTTP Server Version 2.2 文档 [最后更新:2006年3月21日]
- 版本说明
- 从1.3升级到2.0
- 从2.0升级到2.2
- Apache 2.2 新特性概述
- Apache 2.0 新特性概述
- The Apache License, Version 2.0
- 参考手册
- 编译与安装
- 启动Apache
- 停止和重启
- 配置文件
- 配置段(容器)
- 缓冲指南
- 服务器全局配置
- 日志文件
- 从URL到文件系统的映射
- 安全方面的提示
- 动态共享对象(DSO)支持
- 内容协商
- 自定义错误响应
- 地址和端口的绑定(Binding)
- 多路处理模块
- Apache的环境变量
- Apache处理器的使用
- 过滤器(Filter)
- suEXEC支持
- 性能方面的提示
- URL重写指南
- Apache虚拟主机文档
- 基于主机名的虚拟主机
- 基于IP地址的虚拟主机
- 大批量虚拟主机的动态配置
- 虚拟主机示例
- 深入研究虚拟主机的匹配
- 文件描述符限制
- 关于DNS和Apache
- 常见问题
- 经常问到的问题
- Apache的SSL/TLS加密
- SSL/TLS高强度加密:绪论
- SSL/TLS高强度加密:兼容性
- SSL/TLS高强度加密:如何...?
- SSL/TLS Strong Encryption: FAQ
- 如何.../指南
- 认证、授权、访问控制
- CGI动态页面
- 服务器端包含入门
- .htaccess文件
- 用户网站目录
- 针对特定平台的说明
- 在Microsoft Windows中使用Apache
- 在Microsoft Windows上编译Apache
- Using Apache With Novell NetWare
- Running a High-Performance Web Server on HPUX
- The Apache EBCDIC Port
- 服务器和支持程序
- httpd - Apache超文本传输协议服务器
- ab - Apache HTTP服务器性能测试工具
- apachectl - Apache HTTP服务器控制接口
- apxs - Apache 扩展工具
- configure - 配置源代码树
- dbmmanage - 管理DBM格式的用户认证文件
- htcacheclean - 清理磁盘缓冲区
- htdbm - 操作DBM密码数据库
- htdigest - 管理用于摘要认证的用户文件
- httxt2dbm - 生成RewriteMap指令使用的dbm文件
- htpasswd - 管理用于基本认证的用户文件
- logresolve - 解析Apache日志中的IP地址为主机名
- rotatelogs - 滚动Apache日志的管道日志程序
- suexec - 在执行外部程序之前切换用户
- 其他程序
- 杂项文档
- 与Apache相关的标准
- Apache模块
- 描述模块的术语
- 描述指令的术语
- Apache核心(Core)特性
- Apache MPM 公共指令
- Apache MPM beos
- Apache MPM event
- Apache MPM netware
- Apache MPM os2
- Apache MPM prefork
- Apache MPM winnt
- Apache MPM worker
- Apache模块 mod_actions
- Apache模块 mod_alias
- Apache模块 mod_asis
- Apache模块 mod_auth_basic
- Apache模块 mod_auth_digest
- Apache模块 mod_authn_alias
- Apache模块 mod_authn_anon
- Apache模块 mod_authn_dbd
- Apache模块 mod_authn_dbm
- Apache模块 mod_authn_default
- Apache模块 mod_authn_file
- Apache模块 mod_authnz_ldap
- Apache模块 mod_authz_dbm
- Apache模块 mod_authz_default
- Apache模块 mod_authz_groupfile
- Apache模块 mod_authz_host
- Apache模块 mod_authz_owner
- Apache模块 mod_authz_user
- Apache模块 mod_autoindex
- Apache模块 mod_cache
- Apache模块 mod_cern_meta
- Apache模块 mod_cgi
- Apache模块 mod_cgid
- Apache模块 mod_charset_lite
- Apache模块 mod_dav
- Apache模块 mod_dav_fs
- Apache模块 mod_dav_lock
- Apache模块 mod_dbd
- Apache模块 mod_deflate
- Apache模块 mod_dir
- Apache模块 mod_disk_cache
- Apache模块 mod_dumpio
- Apache模块 mod_echo
- Apache模块 mod_env
- Apache模块 mod_example
- Apache模块 mod_expires
- Apache模块 mod_ext_filter
- Apache模块 mod_file_cache
- Apache模块 mod_filter
- Apache模块 mod_headers
- Apache模块 mod_ident
- Apache模块 mod_imagemap
- Apache模块 mod_include
- Apache模块 mod_info
- Apache模块 mod_isapi
- Apache模块 mod_ldap
- Apache模块 mod_log_config
- Apache模块 mod_log_forensic
- Apache模块 mod_logio
- Apache模块 mod_mem_cache
- Apache模块 mod_mime
- Apache模块 mod_mime_magic
- Apache模块 mod_negotiation
- Apache模块 mod_nw_ssl
- Apache模块 mod_proxy
- Apache模块 mod_proxy_ajp
- Apache模块 mod_proxy_balancer
- Apache模块 mod_proxy_connect
- Apache模块 mod_proxy_ftp
- Apache模块 mod_proxy_http
- Apache模块 mod_rewrite
- Apache模块 mod_setenvif
- Apache模块 mod_so
- Apache模块 mod_speling
- Apache模块 mod_ssl
- Apache模块 mod_status
- Apache模块 mod_suexec
- Apache模块 mod_unique_id
- Apache模块 mod_userdir
- Apache模块 mod_usertrack
- Apache模块 mod_version
- Apache模块 mod_vhost_alias
- Developer Documentation for Apache 2.0
- Apache 1.3 API notes
- Debugging Memory Allocation in APR
- Documenting Apache 2.0
- Apache 2.0 Hook Functions
- Converting Modules from Apache 1.3 to Apache 2.0
- Request Processing in Apache 2.0
- How filters work in Apache 2.0
- Apache 2.0 Thread Safety Issues
- 词汇和索引
- 词汇表
- 指令索引
- 指令速查
- 模块索引
- 站点导航