沈阳工程学院毕业论文 第4章 Apache模块化体系结构
在该宏中,,ret是定义挂钩的返回类型,而name则是定义挂钩的名称;args则是挂钩函数需要的额外参数,通常以bracket形势出现。例如下面的语句就声明了一个返回类型为整数,函数名称为do_something,函数参数为(request_rec *r,int n)的挂钩;
AP_DECLARE_HOOK(int, do_something, (request_rec *r,int n)) 不过,AP_DECLARE_HOOK内部则是调用的APR_DECLARE_EXTERNAL _HOOK宏,该宏定义如下:
#define APR_DECLARE_EXTERNAL_HOOK(ns,link,ret,name,args) \\ typedef ret ns##_HOOK_##name##_t args; \\
link##_DECLARE(void) ns##_hook_##name(ns##_HOOK_##name##_t *pf, \\ const char * const *aszPre, \\
const char * const *aszSucc, int nOrder); \\ link##_DECLARE(ret) ns##_run_##name args; \\
APR_IMPLEMENT_HOOK_GET_PROTO(ns,link,name); \\ typedef struct ns##_LINK_##name##_t \\ { \\
ns##_HOOK_##name##_t *pFunc; \\ const char *szName; \\
const char * const *aszPredecessors; \\ const char * const *aszSuccessors; \\ int nOrder; \\
} ns##_LINK_##name##_t;
该宏进一步替换展开后为APR_DECLARE_EXTERNAL_HOOK(ap,AP,int, post_config,(apr_pool_t *pconf,apr_pool_t *plog,apr_pool_t *ptemp,server_rec *s) ),声明一个挂钩宏需要五部分内容。
(1)定义挂钩的执行函数原型:typedef ret ns##_HOOK_##name##_t args; (2)定义挂钩注册函数的原型:
link##_DECLARE(void) ns##_hook_##name(ns##_HOOK_##name##_t *pf, \\ const char * const *aszPre, \\
const char * const *aszSucc, int nOrder); \\ (3)声明挂钩的调用函数:ink##_DECLARE(ret) ns##_run_##name args; (4)APR_IMPLEMENT_HOOK_GET_PROTO(ns,link,name); 该宏展开如下所示:
#define APR_IMPLEMENT_HOOK_GET_PROTO(ns,link,name) \\ link##_DECLARE(apr_array_header_t *) ns##_hook_get_##name(void) 该宏用于返回挂钩访问函数原型,在模块外部可以调用该函数获得注册为该挂钩的所有函数。
(5)typedef struct ns##_LINK_##name##_t \\ { \\
ns##_HOOK_##name##_t *pFunc; \\
- 30 -
沈阳工程学院毕业论文 第4章 Apache模块化体系结构
const char *szName; \\
const char * const *aszPredecessors; \\ const char * const *aszSuccessors; \\ int nOrder; \\
} ns##_LINK_##name##_t;
该宏定义了一个结构类型,用来保存挂钩的相关信息,这些信息与在挂钩定义宏中的信息完全相同。
至此,与挂钩关联的五个方面都已经声明完毕了。
4.4.2 挂钩数组声明
在Apache中,系统定义了一定数量的挂钩,这些挂钩总的来说可以分为两大类:启动挂钩和请求挂钩。启动挂钩是随着服务器启动进行调用的挂钩。而请求挂钩则是服务器处理请求时进行调用的挂钩。对于同一个挂钩,不同模块对应的处理函数各不相同,为了能够保存各个模块中对同一挂钩的实用信息,Apache核心使用apr_array_header_t数组进行保存,该书组通过宏APR_HOOK_LINK声明:
#define APR_HOOK_LINK(name) \\ apr_array_header_t *link_##name;
该宏定义很简单,实际上就是声明了一个apr_array_header_t类型数组,用来保存对应某个挂钩的所有挂钩函数。
4.4.3 挂钩结构
对于每一个挂钩,Apache都会顶一个apr_array_header_t数组来保存它的相关信息。一旦定义了挂钩数组,该数组将在整个Apache中保持唯一。当某个模块想要使用该挂钩的时候,只要访问模块内对应的挂钩数组即可。
不过,Apache2.0中并不支持直接访问挂钩数组,因此,你想直接将数据压入数组是不可能的。Apache2.0中引入了APR_HOOK_STRUCT宏,所有对数组的操作都只能通过该红来实现。该宏定义如下:
#define APR_HOOK_STRUCT(members) \\ static struct { members } _hooks;
该宏展开后实际上定义了一个限于模块内使用的结构_hooks,该模块内部实现的所有挂钩的对应数组都保存为_hook的成员。
Apache中对挂钩数组的访问都必须通过_hook来实现。 不过有几点必须注意的是:
(1)_hook在某个文件中只能定义一次。
(2)_hook的定义为static,这意味着该结构实际上是模块内部的私有结构,外部模块无法直接访问_hook结构。
- 31 -
沈阳工程学院毕业论文 第4章 Apache模块化体系结构
4.4.4 挂钩函数注册
从宏的名字我们就可以大体看出,该宏实际上是实现了具体的挂钩注册函数。该宏定义如下所示:
#define APR_IMPLEMENT_EXTERNAL_HOOK_BASE(ns,link,name) \\
link##_DECLARE(void) ns##_hook_##name(ns##_HOOK_##name##_t *pf,
const char * const *aszPre, \\
const char * const *aszSucc,int nOrder) \\ { \\
ns##_LINK_##name##_t *pHook; \\ if(!_hooks.link_##name) \\
{ \\
_hooks.link_##name=apr_array_make(apr_hook_global_pool,1,sizeof(ns##_LINK_##name##_t)); \\
apr_hook_sort_register(#name,&_hooks.link_##name); \\ } \\
pHook=apr_array_push(_hooks.link_##name); \\ pHook->pFunc=pf; \\
pHook->aszPredecessors=aszPre; \\ pHook->aszSuccessors=aszSucc; \\ pHook->nOrder=nOrder; \\ pHook->szName=apr_hook_debug_current; \\ if(apr_hook_debug_enabled) \\
apr_hook_debug_show(#name,aszPre,aszSucc); \\ } \\
APR_IMPLEMENT_HOOK_GET_PROTO(ns,link,name) \\ { \\
return _hooks.link_##name; \\ }
挂钩注册函数首先检查挂钩数组是否为空,如果为空,则说明是第一次注册该挂钩,然后创建新数组并注册该挂钩类型以供以后排序用;否则,直接加入一条新纪录。
4.4.5 使用挂钩
Apache中挂钩调用函数形式通常如ap_run_HOOKNAME所示,比如ap_run_post_config就是调用post_config挂钩。尽管所有的挂钩对外提供的形式都是一样的,但是内部实现却不相同,差别分别体现于三个宏:AP_IMPLE- MENT_HOOK_VOID、AP_IMPLEMENT_HOOK_RUN_FIRST及AP_IMPLEM- ENT_HOOK_RUN_ALL。
- 32 -
沈阳工程学院毕业论文 第4章 Apache模块化体系结构
1.对于AP_IMPLEMENT_HOOK_VOID,调用函数将遍历挂钩数组,逐个执行针对该挂钩的所有注册过的挂钩函数,直到遍历调用结束。这种类型通常称为VOID,由于它没有任何返回值,其声明如下:
#define
APR_IMPLEMENT_EXTERNAL_HOOK_VOID(ns,link,name,args_decl,args_use) \\
APR_IMPLEMENT_EXTERNAL_HOOK_BASE(ns,link,name) \\ link##_DECLARE(void) ns##_run_##name args_decl \\ { \\
ns##_LINK_##name##_t *pHook; \\ int n; \\ \\
if(!_hooks.link_##name) \\ return; \\ \\
pHook=(ns##_LINK_##name##_t *)_hooks.link_##name->elts; \\ for(n=0 ; n < _hooks.link_##name->nelts ; ++n) \\ pHook[n].pFunc args_use; \\ } 2.AP_IMPLEMENT_HOOK_RUN_ALL简称ALL类型,它与AP_IMPLEME- NT _ HOOK_VOID几乎相同,唯一不同的是ALL类型具有返回值。宏声明如下:
#define APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(ns,link,ret,name,args_decl,
args_use,ok,decline) \\
APR_IMPLEMENT_EXTERNAL_HOOK_BASE(ns,link,name) \\ link##_DECLARE(ret) ns##_run_##name args_decl \\ { \\
ns##_LINK_##name##_t *pHook; \\ int n; \\ ret rv; \\ \\
if(!_hooks.link_##name) \\
return ok; \\ \\
pHook=(ns##_LINK_##name##_t *)_hooks.link_##name->elts; \\ for(n=0 ; n < _hooks.link_##name->nelts ; ++n) \\ { \\
rv=pHook[n].pFunc args_use; \\ \\
if(rv != ok && rv != decline) \\ return rv; \\
- 33 -
沈阳工程学院毕业论文 第4章 Apache模块化体系结构
} \\
return ok; \\
}
3.AP_IMPLEMENT_HOOK_RUN_FIRST简称FIRST类型,对于该类型,Apache核心从头逐一遍历挂钩数组中所注册的挂钩函数,直到遇到一个能够完
成所提交任务的函数或发生错误为止。该宏定义如下:
#define
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(ns,link,ret,name,args_decl,
args_use,decline) \\
APR_IMPLEMENT_EXTERNAL_HOOK_BASE(ns,link,name) \\ link##_DECLARE(ret) ns##_run_##name args_decl \\
{ \\
ns##_LINK_##name##_t *pHook; \\ int n; \\ ret rv; \\ \\
if(!_hooks.link_##name) \\ return decline; \\ \\
pHook=(ns##_LINK_##name##_t *)_hooks.link_##name->elts; \\ for(n=0 ; n < _hooks.link_##name->nelts ; ++n) \\
{ \\
rv=pHook[n].pFunc args_use; \\ \\
if(rv != decline) \\ return rv; \\ } \\
return decline; \\ }
任何挂钩都必须且只能是三类型中的一种,任何挂钩在被执行之前都必须调用这三个宏中的一个进行声明。
- 34 -