沈阳工程学院毕业论文 第4章 Apache模块化体系结构
modp = ap_preloaded_modules[i];
if (memcmp(modp->name, \ continue;}
preload_name = modp->name + strlen(\ preload_len = strlen(preload_name) - 2;
if (strlen(modname) <= strlen(\ continue;}
thismod_len = strlen(modname) - strlen(\ if (strcmp(modname + thismod_len, \ continue;}
if (thismod_len != preload_len) {
continue;}
if (!memcmp(modname, preload_name, preload_len)) { return apr_pstrcat(cmd->pool, \ \ NULL);} }
如果制定的模块没有被加载,那么函数将执行加载。对动态加载模块检查完毕后,Apache将检查静态链接模块数组ap_preloaded_modules。在ap_preloaded_modules数组中查找指定的模块相对简单。对于每一个模块,要阿Apache认定他是一个合法的模块,就必须符合:
(1)Apache必须保证其文件名是以\开始的。 (2)Apache必须保证其文件名是以\结束的。 modi = apr_array_push(sconf->loaded_modules); modi->name = modname; if (apr_dso_load(&modhandle, szModuleFile, cmd->pool) != APR_SUCCESS) {
char my_error[256];
return apr_pstrcat(cmd->pool, \ \
apr_dso_error(modhandle, my_error, sizeof(my_error)),
NULL);}
ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, cmd->pool, \
if (apr_dso_sym(&modsym, modhandle, modname) != APR_SUCCESS) { char my_error[256];
return apr_pstrcat(cmd->pool, \ modname, \ apr_dso_error(modhandle, my_error,
- 25 -
沈阳工程学院毕业论文 第4章 Apache模块化体系结构
sizeof(my_error)),
NULL);}
modp = (module*) modsym;
modp->dynamic_load_handle = (apr_dso_handle_t *)modhandle; modi->modp = modp;
一切检查完毕后,函数将对模块进行加载。加载分为两步骤。 (1)在module_so模块中保存当前新载入的模块信息。
(2)调用apr_dso_load将文件载入到Apache的地址空间中,同时调用apr_dso_sym获取动态库中的module结构,返回的结构保存在modsym中。
if (modp->magic != MODULE_MAGIC_COOKIE) {
return apr_psprintf(cmd->pool, \ \garbled - expected signature lx but saw \
\- perhaps this is not an Apache module DSO, \
\was compiled for a different Apache version?\
modname, szModuleFile, MODULE_MAGIC_COOKIE, modp->magic);}
error = ap_add_loaded_module(modp, cmd->pool); if (error) {
return error;}
在真正使用即激活加载模块前,Apache必须确保加载的模块确实是Apache没快。为此Apache在模块结构中设定了magic字段,通过检查magic字段,Apache确定加载的模块是否是Apache模块。如果加载的是合法的Apache模块,函数将立即调用ap_add_loaded_module蒋模块激活,所谓的激活无非是将模块放入ap_top_mudoles链表中。
apr_pool_cleanup_register(cmd->pool, modi, unload_module, apr_pool_cleanup_null);
ap_single_module_configure(cmd->pool, cmd->server, modp); return NULL; }
此外,Apache还需要在配置内存池pconf中注册cleanup函数。这样当我们重新启动或关闭服务器时,cleanup函数将自动调用并共享模块卸载。
动态加载模块最后一步是调用模块内部的create_server_config和create_dir_config指针创建模块服务器的相关配置和目录相关配置。它们的创建封装在ap_single_module_configure中。
实际的核心加载由ap_add_loaded_module完成,该函数的实现相对简单,如下:
- 26 -
沈阳工程学院毕业论文 第4章 Apache模块化体系结构
AP_DECLARE(const char *) ap_add_loaded_module(module *mod, apr_pool_t *p)
{
module **m; const char *error;
error = ap_add_module(mod, p); if (error) {
return error;}
for (m = ap_loaded_modules; *m != NULL; m++) ;
*m++ = mod;
*m = NULL; return NULL; }
ap_add_loaded_module函数完成的事情很简单,一方面它调用ap_add_module将模块增加到ap_top_module模块链表中,另一方面还要将模块增加到ap_loaded_modules加载模块链表中。
4.2.3 模块卸载
当一个模块不要再使用时,该模块就可以被卸载。Apache中并没有提供显示式的卸载模块的指令和接口,也就是说,一个模块一旦被加载,就无法再运行过程中被卸载。卸载的唯一办法就是修改配置文件或重新编译。
由于模块数据结构中所占用的内存也是从内存池中分配出来的,因此它遵循内存池的清理原则。当内存被释放时,模块数据结构调用自身的清理数据结构执行模块清理。模块的清理通过函数unload_module完成:
static apr_status_t unload_module(void *data){
ap_module_symbol_t *modi = (ap_module_symbol_t*)data; if (modi->modp == NULL) return APR_SUCCESS;
ap_remove_loaded_module(modi->modp); modi->modp = NULL; modi->name = NULL; return APR_SUCCESS; }
实际的模块卸载由ap_remove_loaded_module函数完成,该函数需要一个参数,即模块的数据描述结构:
AP_DECLARE(void) ap_remove_loaded_module(module *mod){ module **m; module **m2;
- 27 -
沈阳工程学院毕业论文 第4章 Apache模块化体系结构
int done;
ap_remove_module(mod);
for (m = m2 = ap_loaded_modules, done = 0; *m2 != NULL; m2++) { if (*m2 == mod && done == 0) done = 1;
else
*m++ = *m2;} *m = NULL; }
整个移除过程分两步,首先必须从模块链表ap_top_module中将模块结构移除,该移除由ap_remove_module完成。
当模块从ap_top_module链表中被移除时,ap_loaded_modules中指向该模块的指针也要被设置为NULL。
4.3 指令表
4.3.1 指令表概述
模块的另外一项功能就是指令表。所谓指令表就是保存各个指令名称及该指令处理函数的数组,图4-1展示的是一个完整的指令构成。
图4-1 指令表构成
指令名称(name)指令处理函数(func)指令参数(cmd_data)指令覆盖类型(cmd_override)指令处理方式(cmd_how)指令返回提示(errmsg)一个指令包含六部分内容,但与模块最紧密相连的是前两个成员,后面的四个成员都是指令本身的信息。
4.3.2 指令处理函数
关于指令处理函数,它与普通的函数没有太多区别,只有两个特殊的地方:一是参数,另一个是返回值。
- 28 -
沈阳工程学院毕业论文 第4章 Apache模块化体系结构
1.参数
所有的指令处理函数,它的前两个参数都是cmd_parms和void*类型,这可以从指令处理函数的类型看出:
typedef union {
const char *(*no_args) (cmd_parms *parms, void *mconfig);
const char *(*raw_args) (cmd_parms *parms, void *mconfig, const char *args);
const char *(*take_argv) (cmd_parms *parms, void *mconfig, int argc, char *const argv[]);
const char *(*take1) (cmd_parms *parms, void *mconfig, const char *w); const char *(*take2) (cmd_parms *parms, void *mconfig, const char *w, const char *w2);
const char *(*take3) (cmd_parms *parms, void *mconfig, const char *w, const char *w2, const char *w3);
const char *(*flag) (cmd_parms *parms, void *mconfig, int on); } cmd_func;
第一个参数是cmd_parms类型,定义在httpd_config.h中。cmd_parms结果会包含各种信息,所有信息至少会适用于一个指令,它是一个大杂烩,只要处理函数中可能需要的参数都会杯赛导致该结构体中。
除了cmd_parms参数外,指令处理函数的另外一个参数就是mconfig,它是void指针类型。各个指令处理函数所需要的参数不尽相同,可以通过mconfig传入。
2.返回值
指令处理函数的返回值只有三种:NULL、DECLINE_CMD及错误字符串。 如果指令处理函数能正常处理完某个指令,那么函数将返回NILL;如果在处理过程中发生了错误,那么函数将返回一个字符串用于描述发生的错误。
第三种情况比较难理解,就是返回DECLINE_CMD,它被定义为“\\a\\b\\”。该返回值在极少数场合下才会被使用。
4.4 挂钩(HOOK)
4.4.1 声明挂钩
Apache中关于挂钩的实现大部分是通过宏来完成的,而且这些宏大多数非常复杂。挂钩的实现主要定义在文件apr_hook.h和apr_hook.c中,另外在congfig.c中也有部分定义。
Apache中对挂钩的使用总是从定义一个挂钩开始的,在Apache中声明一个挂钩,总是通过如下的宏来实现的。
#define AP_DECLARE_HOOK(ret,name,args) \\
APR_DECLARE_EXTERNAL_HOOK(ap,AP,ret,name,args)
- 29 -