动态网站制作指南 [  QQ表情  ]
[ 投票调查 ]
[ 企业邮箱 ]
[ 网站空间 ]
网络编程 | 站长之家 | 网页制作 | 图形图象 | 操作系统 | 冲浪宝典 | 软件教学 | 网络办公 | 邮件系统 | 网络安全 | 认证考试 | 系统进程
ASP源码 | .Net源码 | PHP源码 | JSP源码 | JAVA源码 | CGI源码 | VB源码 | C++源码 | Delphi源码 | PB源码 | VF源码 | 汇编 | 服务器
Firefox | IE | Maxthon | 迅雷 | 电驴 | BitComet | FlashGet | QQ | QQ空间 | Vista | 输入法 | Ghost | Word | Excel | wps | Powerpoint
asp | .net | php | jsp | Sql | c# | Ajax | xml | Dreamweaver | FrontPages | Javascript | css | photoshop | fireworks | Flash | Cad | Discuz!
当前位置 > 网站建设学院 > 网络编程 > 软件工程
Tag:注入,存储过程,分页,安全,优化,xmlhttp,fso,jmail,application,session,防盗链,stream,无组件,组件,md5,乱码,缓存,加密,验证码,算法,cookies,ubb,正则表达式,水印,索引,日志,压缩,base64,url重写,上传,控件,Web.config,JDBC,函数,内存,PDF,迁移,结构,破解,编译,配置,进程,分词,IIS,Apache,Tomcat,phpmyadmin,Gzip,触发器,socket
文章搜索服务
邮件订阅
输入你的邮件地址,
你将不会错过任何关于:
[ 软件工程 ]的信息

本月文章推荐
.软件开发管理与质量控制(一).
.VS2005与SQL Server2005的整合优.
.Ruby程序设计之简单例子.
.Vista新技术:WCF开发指南之客户.
.SOA的进化(三)SOA的根源(1).
.跨平台的SOA实施工具BEA AquaLog.
.系统分析员基本功.
.SOA破解集成难题 向新一代软件架.
.使用模式集成UML视图.
.软件项目质量管理经验谈.
.解决弹出窗口及AdWare.HBang(第.
.统一建模语言UML概述.
.系统设计前的需求探索.
.用UML描述Java类.
.Hibernate的检索策略小结.
.2006拭目以待 SOA标准走向成熟.
.基于UML的短消息计费系统的分析与.
.EMF模型解析的策略分析.
.软件架构训练之层次及使用.
.企业架构和SOA架构的角色将融合.

Apache中的挂钩剖析(2)

发表日期:2008-3-23 |


5.5.5 挂钩函数(APR_IMPLEMENT_EXTERNAL_HOOK_BASE)
从宏的名字我们就可以大体看出该宏实际上是实现了具体的挂钩注册函数,假如将其展开后我们会更加一目了然。该宏的定义也是冗长的很,如下所示:
#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; \
}
对于post_config挂钩,我们该宏展开的结果如下:
AP_DECLARE(int) ap_hook_post_config(ap_HOOK_post_config_t *pf,
                                    const char * const *aszPre,
                                    const char * const *aszSucc,

                                    int nOrder)
{
    ap_LINK_post_config_t *pHook;
    if (!_hooks.link_post_config) {
        _hooks.link_post_config = apr_array_make(apr_hook_global_pool, 1,
                                                 sizeof(ap_LINK_post_config_t));
        apr_hook_sort_register("post_config", &_hooks.link_post_config);
    }
    pHook = apr_array_push(_hooks.link_post_config);
    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("post_config", aszPre, aszSucc);
}
 
AP_DECLARE(apr_array_header_t *) ap_hook_get_post_config(void) {
    return _hooks.link_post_config;
}
从上面的展开结果中我们可以很清楚的看出APR_IMPLEMENT_EXTERNAL_HOOK_BASE宏实现了我们所需要的挂钩注册函数以及挂钩信息获取函数。
挂钩注册函数中的很多代码非常熟悉,一看原来就是前面我们APR_HOOK_STRUCT中用过的示例代码。挂钩注册函数首先检查挂钩数组是否为空,假如为空则说明是第一次注册该挂钩,所以创建新数组并注册该挂钩类型以供以后排序用;否则,直接加入一条记录。
5.5.6 使用挂钩
一旦各个模块在挂钩数组中注册了自己感爱好的挂钩,那么剩下的事情无非就是调用这些挂钩,实际上也就是最终调用挂钩对应的函数。Apache中的挂钩调用函数形式通常如ap_run_HOOKNAME所示,比如ap_run_post_config就是调用post_config挂钩。尽管所有的挂钩对外提供的调用形式都是一样的,但是内部实现却不尽相同,分别体现于三个宏:AP_IMPLEMENT_HOOK_VOID、AP_IMPLEMENT_HOOK_RUN_FIRST以及AP_IMPLEMENT_HOOK_RUN_ALL。

(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; \
    }
比如对于config.c中的child_init挂钩,其就是VOID类型,声明语句如下:
AP_IMPLEMENT_HOOK_VOID(child_init,
                       (apr_pool_t *pchild, server_rec *s),
                       (pchild, s))
撇去APR_IMPLEMENT_EXTERNAL_HOOK_BASE(ns,link,name)不管,这里我们仅仅关心剩下的的展开结果,如下:
     AP_DECLARE(void) ap_run_child_init(apr_pool_t *pchild,server_rec* s)
     {
         ap_LINK_child_init_t pHook;
         int n;
         if(!_hooks.link_child_init)
              return;
         pHook=(ap_LINK_child_init_t)_hooks.link_child_init->elts;
         for(n=0;n<_hooks.link_child_init->nelts;++n)
              pHook[n].pFunc(pchild, s);

     }
从展开结果可以看出,即使在逐次调用过程中发生了错误,调用也不会停止,它就是“一头拉不回头的牛”。
(2)、AP_IMPLEMENT_HOOK_ALL简称ALL类型,其与AP_IMPLEMENT_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; \
     } \
    return ok; \
    }
open_logs挂钩就是属于这种类型,其在config.c中的定义如下:
AP_IMPLEMENT_HOOK_RUN_ALL(int, open_logs,
                          (apr_pool_t *pconf, apr_pool_t *plog,
                           apr_pool_t *ptemp, server_rec *s),
                          (pconf, plog, ptemp, s), OK, DECLINED)
因此将它展开后的结果如下:
AP_DECLARE(int) ap_run_open_logs(apr_pool_t *pconf, apr_pool_t *plog,
                           apr_pool_t *ptemp, server_rec *s)

{
    ap_LINK_open_logs_t *pHook;
    int n;
    ret rv;
    if(!_hooks.link_open_logs)
          return ok;
    pHook=(ap_LINK_open_logs_t *)_hooks.link_open_logs->elts; \
    for(n=0 ; n < _hooks.link_open_logs->nelts ; ++n) \
     {
          rv=pHook[n].pFunc(pconf, plog, ptemp, s);
          if(rv != ok && rv != decline) \
              return rv;
     }
    return ok;
}
从展开的结果来看,ALL类型在对挂钩数组进行遍历调用的时候,即使调用的请求被“DECLINE”,调用也将继续;只有调用请求发生错误才返回该错误值,同时退出遍历。
(3)、AP_IMPLEMENT_HOOK_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; \
}
quick_handler就是属于该类型的挂钩,其在config.c中定义如下:
AP_IMPLEMENT_HOOK_RUN_FIRST(int, quick_handler, (request_rec *r, int lookup),
                            (r, lookup), DECLINED)
该宏展开后的结果如下所示:
AP_DECLARE(ret) ap_run_quick_handler(request_rec *r, int lookup)
{
    ap_LINK_quick_handler_t  *pHook;
    int n;
    ret rv;
    if(!_hooks.link_quick_handler)
          return decline;
    pHook=(ap_LINK_quick_handler_t *)_hooks.link_quick_handler->elts;
    for(n=0 ; n < _hooks.link_quick_handler->nelts ; ++n) \
     {
     rv=pHook[n].pFunc(r,look_up);
     if(rv != decline)
         return rv;
     }
    return decline;
}
从展开结果中可以看出,在遍历调用过程中一旦找到合适的函数,即rv!=decline的时候,函数即返回,不再继续遍历调用,即使后面仍然有合法的可调用函数。
任何挂钩都必须而且只能是三种类型中的一种。任何挂钩在被真正执行之前都必须调用这三个宏中的一个进行声明。
5.5.6 编写自己挂钩
使用挂钩所带来的最大的一个好处就是可以自行增加挂钩,而不需要全局统一。下面的部分,我们通过编写一个简单的挂钩同时在模块中使用该挂钩从而来加深理解上面所分析的内容。比如现在我们定义在了一个能够在Apache处理请求时调用的函数some_hook,其函数原型为:
int some_hook(request_rec* r,int n);
那么我们分为四个步骤来声明我们的挂钩。
(1)、挂钩声明
我们使用AP_DECLARE_HOOK宏声明一个名称为some_hook的挂钩,声明如下:
AP_DECLARE_HOOK(int,some_hook,(request_rec* r,int n))
(2)、挂钩数组声明
由于some_hook是新声明的挂钩,为此,我们必须同时声明新的挂钩数组来保存各个模块对挂钩的注册使用。声明的语句如下:
     APR_HOOK_STRUCT(
     APR_HOOK_LINK(some_hook)

     ……
     )
(3)、声明函数调用类型
挂钩声明完毕之后,真正被ap_run_name调用之前,我们还必须声明挂钩的调用类型,或者是VOID类型,或者是FIRST类型,或者是ALL类型。此处我们声明some_hook挂钩为VOID类型,声明语句如下:
     AP_IMPLEMENT_HOOK_RUN_VOID(some_hook,(request_rec* r,int n),(r,n))
定义完该宏,实际上也对外声明了该挂钩的执行函数ap_run_some_hook。
(4)、注册挂钩函数
至第三步为止,对挂钩的声明已经基本结束,也意味着下一步的工作实际上应该落实到挂钩使用者身上了。比如假如模块som_module中想使用挂钩some_hook,则其必须在挂钩注册函数中注册该挂钩,挂钩注册函数为ap_hook_some_hook,代码示例如下:
static void register_hooks()
{
     ……
     ap_hook_some_hook(some_hook_function,NULL,NULL,HOOK_MIDDLE);
}
AP_DECLARE_DATA module core_module = {
    ……
    register_hooks                /* register hooks */
};
(5)、编写挂钩函数
最后剩下的内容就是编写具体的挂钩调用函数。比如在some_module模块中,我们希望挂钩函数只是打印出“Hello World”语句,而且从(4)中看出挂钩函数名称为some_hook_function,因此挂钩函数声明为如下:
static void some_hook_function(request_rec* r,int n)
{
     ap_rputs(“Hello World\n”);
     return;
}
需要注重的是,这边的挂钩函数必须符合AP_IMPLEMENT_HOOK_RUN_XXX中声明的格式。

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

假如你觉得本文不错,请点击文后的“推荐本文”链接!!

上一篇:Apache中的挂钩剖析(3) 人气:401
下一篇:要意识到僵尸网络日益增长的威胁 人气:351
浏览全部软件工程的内容 Dreamweaver插件下载 常用网页广告代码全集
  最新网站源码 最新软件下载
2008-10-12 team论坛 v2.0.4 bulid 080916 A
2008-10-12 Roclog v3.1.6
2008-10-12 SupeV v1.0.1 简体中文 GBK
2008-10-12 NetCMS v1.6.0.1010 正式版
2008-10-12 PHP考试系统PPFrame v1.2.7
2008-10-12 LPAS个人相册 v1.6.3
2008-10-12 快问仿百度知道系统 动态-静态-互
2008-10-12 方卡广告防点击系统 V1.0 GB2312
2008-10-12 泡菜内容管理系统[PCMS] v1.0 Bu
2008-10-11 联系人分组工具 v1.1 中文破解版
2008-10-11 FaceMelter变脸 v2.0 汉化破解版
2008-10-11 PathTracker道路跟踪仪 v1.2 破解
2008-10-11 Rooms手机聊天室 v0.6.7 破解版
2008-10-11 RemoteDesktop远程桌面 v1.0 破解
2008-10-11 ProRemote远程调音台 v1.0.1 破解
2008-10-11 PicShare照片共享 v1.0.0 破解版
2008-10-11 Photogene照片编辑器 v1.5 汉化破
2008-10-11 WriteRoom共享文档 v1.0 破解版
  发表评论
姓 名: 验证码:
内 容:
站长工具:网站收录查询 | Google PR查询 | ALEXA排名查询 | CSS在线编辑器 | 广告代码 | js/vbs加密 | md5加密 | 进制转换 | UTF-8 转换工具 | Html转换js | Html转换asp | Html转换php | Html转换perl
实用工具:汉字翻译拼音 | 拼音字典 | 符号对照表 | 个税计算 | 实时汇率查询换算 | 经典小工具 | 汉字简繁转换 | 普通单位换算 | 公制单位换算 | 生辰老黄历 | 国内电话区号 | 国家代码与域名缩写 | 文字加密解密 | 健康查询 | 万年历 | 汉字横竖排版 | 手机号码查询 | 计算器 | ip搜索
业务联系 | 广告刊登 | 频道合作 | 投稿荐稿 | 联系方式 | 加入收藏 | RSS订阅
Copyright © 2000-2008 www.knowsky.com All rights reserved | 网络实名:动态网站制作指南 | 沪ICP备05001343号
ホームページ制作 不動産検索システム 求人情報
防水工事·改修工事 フットサル大会 探偵
SEO対策 中国語教室 ホームページ作成