China Open source community
站内导航:

 
 
 
当前位置: 首页 >> 应用软件 >> 网络相关 >> Apache中的挂钩剖析(3)
 

Apache中的挂钩剖析(3)

作者:flydish1234 at sina.com.cn      来源:     发表时间:2006-03-06     浏览次数:      字号:    

bsp;                 (r, flags),

                                    OK, DECLINED)
     该宏展开后即实现了ap_run_status_hook函数,其实现过程,即展开结果如下所示:
     AP_DECLARE(int) ap_run_status_hook(request_rec* r,int flags)
     {
         ap_LINK_status_hook_t *pHook;
         int n;
         int rv;
    apr_array_header_t *pHookArray=apr_optional_hook_get(status_name);
    if(!pHookArray)
return ok;
    pHook=(ap_LINK_status_hook_t *)pHookArray->elts;
    for(n=0 ; n < pHookArray->nelts ; ++n)
{
rv=(pHook[n].pFunc)(r,flags);
if(rv != ok && rv != decline)
    return rv;
}
    return ok;
     }
5.5.8 可选函数
与挂钩存在类似的问题,函数也可能存在可选挂钩的问题。如果Apache在调用某一个函数的时候,该函数尚未被加载,会发生什么呢。你可能觉得DSO是个好的解决方法。如果指定的函数没有,则动态加载DSO模块进行查找。不过这种策略并不但是最完美的方案。首先,不是所有的平台都支持DSO;另一方面,
可选函数的特点正如其名,这就决定了它相对于Apache的动态性。正常的函数都是编写之后才会加入Apache中进行编译,而且一旦编译就无法更改。而可选函数则是在需要的时候动态产生的。在产生之前,没有人为之进行过专门的编写,因此链接代码中自然也就找不到该函数的实现。
可选函数的使用可以分为五大步骤:声明、实现、注册、获取以及函数调用。
5.5.8.1.可选函数的声明
声明一个可选函数通过宏APR_DECLARE_OPTIONAL_FN来实现,比如我们如果想声明一个optional_fun可选函数,其返回int类型,参数需要字符串类型,那么声明可以如下:
APR_DECLARE_OPTIONAL_FN(int,optional_fun,(const char* params))
APR_DECLARE_OPTIONAL_FN宏定义如下:
 #define APR_DECLARE_OPTIONAL_FN(ret,name,args) \
typedef ret (APR_OPTIONAL_FN_TYPE(name)) args
如果将上面的宏展开,则可以看出,该宏只是声明了一个apr_OFN_optional_fun_t类型的函数指针:
typedef int (apr_OFN_optional_fun_t)(const char* params)
一旦声明完毕,我们则将其进行实现如下,在实现中必须注意名称以及函数参数类型的匹配:
int optional_fun(const char* params)
{
       ……
       return 0;
}
5.5.8.2.可选函数的注册
可选函数由Apache核心统一维护。与可选挂钩类似,Apache核心维护了一个全局的哈希表s_phOptionalFunctions,该哈希表的键为可选函数的名称,而值则是对应的函数指针。为了便于Apache使用,我们必须在s_phOptionalFunctions中登记可选函数。函数的注册通过APR_REGISTER_OPTIONAL_FN进行,APR_REGISTER_OPTIONAL_FN定义如下:
#define APR_REGISTER_OPTIONAL_FN(name) do { \
 APR_OPTIONAL_FN_TYPE(name) *apu__opt = name; \
 apr_dynamic_fn_register(#name,(apr_opt_fn_t *)apu__opt); \
} while(0)
该宏内部实际调用了函数apr_dynamic_fn_register进行实际的注册。事实上,Apache中提供了相关函数来支持对s_phOptionalFunctions的操作,除了apr_dynamic_fn_register之外,还包括apr_register_optional_fnapr_dynamic_fn_retrieveapr_retrieve_optional_fn。不过其中apr_retrieve_optional_fnapr_register_optional_fnApache2.0中已经被废弃,因此不再多说。
apr_dynamic_fn_register的原型如下:
APU_DECLARE_NONSTD(void) apr_dynamic_fn_register(const char *szName,
                                                  apr_opt_fn_t *pfn)
参数szName是可选函数的名称,pfn则是实际可选函数的指针。如果s_phOptionalFunctions哈希表存在,函数只是简单的调用apr_hash_set将记录插入表中。
apr_dynamic_fn_retrieve函数原型为
APU_DECLARE(apr_opt_fn_t *) apr_dynamic_fn_retrieve(const char *szName)
该函数根据给定的函数名称获取实际的函数指针。
事实上,这两个函数都不允许直接调用,它们作为Apache的内部函数而存在,对外提供的则是宏APR_REGISTER_OPTIONAL_FNAPR_RETRIEVE_OPTIONAL_FN
对于optional_fun可选函数,注册语句如下:
APR_REGISTER_OPTIONAL_FN(optional_fun);
当用户想使用可选函数的时候,首先必须获得其函数指针,用法如下:
APR_OPTIONAL_FN_TYPE(some_fn) *pfn;
pfn=APR_RETRIEVE_OPTIONAL_FN(some_fn);
 
 
5.5.8.3.可选函数的使用
 
5.5.9智能挂钩
5.5.10挂钩工作机制
在前面的部分,我们对挂钩进行了详细的分析,但是还缺乏一个整体上的概念。从整体上来看挂钩的工作机制可以用下图来描述:

一个模块从挂钩的角度来看,其最重要的无非是两个方面:挂钩注册函数和挂钩处理函数。挂钩注册函数通常是模块结构中的register_hooks函数指针,该函数指针将调用实际的挂钩注册函数进行挂钩注册。挂钩注册的过程很简单,通过宏ap_hook_xxx实现。比如上图中声明了两个挂钩abc和xyz。
与此同时,模块中也将声明与挂钩对应的挂钩函数,该挂钩被触发的时候,该函数将被调用。正如前面描述,可以使用宏ap_run_xxx触发指定的挂钩。不过挂钩的触发通常是由核心模块在对客户端请求进行处理的过程中进行。
我们来看一个具体的例子,这是核心模块中关于挂钩的部分。
从模块的结构中可以看出,模块结构中包含一个指针register_hook,该指针通常指向模块中的实际的挂钩注册函数,比如,对于核心模块而言,其挂钩注册函数register_hooks,那么结构中的该指针也为register_hooks:
AP_DECLARE_DATA module core_module = {
    STANDARD20_MODULE_STUFF,
    create_core_dir_config,       /* create per-directory config structure */
    merge_core_dir_configs,       /* merge per-directory config structures */
    create_core_server_config,    /* create per-server config structure */
    merge_core_server_configs,    /* merge per-server config structures */
    core_cmds,                    /* command apr_table_t */
    register_hooks                /* register hooks */
};
而具体的register_hooks函数则如下:
static void register_hooks(apr_pool_t *p)
{
    ap_hook_create_connection(core_create_conn, NULL, NULL,
                              APR_HOOK_REALLY_LAST);
    ap_hook_pre_connection(core_pre_connection, NULL, NULL,
                           APR_HOOK_REALLY_LAST);
 
    ap_hook_post_config(core_post_config,NULL,NULL,APR_HOOK_REALLY_FIRST);
    ap_hook_translate_name(ap_core_translate,NULL,NULL,APR_HOOK_REALLY_LAST);
    ap_hook_map_to_storage(core_map_to_storage,NULL,NULL,APR_HOOK_REALLY_LAST);
    ap_hook_open_logs(ap_open_logs,NULL,NULL,APR_HOOK_REALLY_FIRST);
    ap_hook_handler(default_handler,NULL,NULL,APR_HOOK_REALLY_LAST);
    ap_hook_type_checker(do_nothing,NULL,NULL,APR_HOOK_REALLY_LAST);
    ap_hook_fixups(core_override_type,NULL,NULL,APR_HOOK_REALLY_FIRST);
    ap_hook_access_checker(do_nothing,NULL,NULL,APR_HOOK_REALLY_LAST);
    ap_hook_create_request(core_create_req, NULL, NULL, APR_HOOK_MIDDLE);
    APR_OPTIONAL_HOOK(proxy, create_req, core_create_proxy_req, NULL, NULL,
                      APR_HOOK_MIDDLE);
    ap_hook_pre_mpm(ap_create_scoreboard, NULL, NULL, APR_HOOK_MIDDLE);
……
}
该函数的任务非常的简单,即声明该模块需要实现的挂钩,以及对应的处理函数,供主程序调用。

关于作者
张中庆,目前主要的研究方向是嵌入式浏览器,移动中间件以及大规模服务器设计。目前正在进行Apache的源代码分析,计划出版《Apache源代码全景分析》上下册。Apache系列文章为本书的草案部分,对Apache感兴趣的朋友可以通过flydish1234 at sina.com.cn与之联系!

[1] [2] [3]

编辑 webmaster

 
 
 
评论
 
 
发表
 
姓名: QQ:
性别: MSN:
E-mail: 主页:
评分: 1 2 3 4 5
评论内容:
验证码:
  
  • 请遵守《互联网电子公告服务管理规定》及中华人民共和国其他各项有关法律法规。
  • 严禁发表危害国家安全、损害国家利益、破坏民族团结、破坏国家宗教政策、破坏社会稳定、侮辱、诽谤、教唆、淫秽等内容的评论 。
  • 用户需对自己在使用本站服务过程中的行为承担法律责任(直接或间接导致的)。
  • 本站管理员有权保留或删除评论内容。
  • 评论内容只代表网友个人观点,与本网站立场无关。
  •  
    中国源码网 - WWW.YUANMA.ORG - 中国开放源代码社区