设为首页 收藏本站
查看: 812|回复: 0

[经验分享] Apache Hook机制解析(中)——细节讨论

[复制链接]

尚未签到

发表于 2017-1-6 10:42:03 | 显示全部楼层 |阅读模式
  本文在上文《Apache Hook机制解析(上)——钩子机制的实现》的基础上,对钩子机制的细节了补充分析。

1. 静态变量_hooks

这个变量由宏APR_HOOK_STRUCT所定义,因为是静态的,所以对这个变量的操作只能在同一源文件中实现——也对应的AP_IMPLEMENT_HOOK_RUN_ALL宏必须在APR_HOOK_STRUCT所在的源文件中被定义。

2. AP__开头的宏和APR__开头的宏
APR指Apache Portable Runtime,它是一个基本的库,可以用于Apache HTTP Server,也可用于其他产品;而AP实际上指Apache HTTP Server。

因此AP__开头的宏很多都是对APR__宏的进一层封装,将其封装到Apache HTTP Server的命名空间,如AP_IMPLEMENT_HOOK_RUN_ALL实际上是APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL宏的封装:

#define AP_IMPLEMENT_HOOK_RUN_ALL(ret,name,args_decl,args_use,ok,decline) /
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(ap,AP,ret,name,args_decl, /
args_use,ok,decline)

如果是要在自己写的代码中调用APR库,则必须使用APR__开头的宏。

3. AP_IMPLEMENT_HOOK_RUN_ALL的变种
在apr_hooks.h中的代码中可以看到,除了AP_IMPLEMENT_HOOK_RUN_ALL宏之外,还有两个用于定义钩子的宏:
APR_IMPLEMENT_EXTERNAL_HOOK_VOID
AP_IMPLEMENT_HOOK_RUN_FIRST

第一个宏是为了无参数的钩子函数准备的,对应前面log_transaction的例子,该宏展开后的触发函数代码如下:
AP_DECALRE(void) ap_run_log_transaction (request_rec *r)
{
ap_AP_log_transaction_t *pHook;
int n;

if(!_hooks.link_log_transaction)
return ok;

pHook=(ap_AP_log_transaction_t *)_hooks.link_log_transaction->elts;
for(n=0 ; n < _hooks.link_log_transaction->nelts ; ++n)
  pHook[n].pFunc (r);
}

第二个宏用于执行条件触发——如果某些钩子实现返回了DECLINE(拒绝),则停止执行后续的钩子函数。对应前面log_transaction的例子,该宏展开后的触发函数代码如下:
AP_DECALRE(int) ap_run_log_transaction (request_rec *r)
{
ap_AP_log_transaction_t *pHook;
int n;
ret rv;

if(!_hooks.link_log_transaction)
return decline;

pHook=(ap_AP_log_transaction_t *)_hooks.link_log_transaction->elts;
for(n=0 ; n < _hooks.link_log_transaction->nelts ; ++n)
{
  rv=pHook[n].pFunc (r);

  if(rv != decline)
   return rv;
}
return decline;
}

这里的实现和AP_IMPLEMENT_HOOK_RUN_ALL 不同主要在于:
1) 默认的返回是decline,而不是ok
2) 只要钩子函数返回decline,则终止循环

4. ap_hook_get_log_transaction
进行钩子的声明和定义时,除了ap_hook_ log_transaction和ap_run_ log_transaction,还有一个函数——ap_hook_get_log_transaction。

这个函数的用处只是提供给外界一个访问log_transaction相关的数据结构的方法。在整个Apache代码中,ap_hook_get_log_transaction只被引用到了一次——在mod_info中,用于遍历log_transaction的钩子函数数组,进行查找。

5. ap_hook_log_transaction的其他参数
AP_DECLARE(void) ap_hook_log_transaction(ap_HOOK_log_transaction_t *pf,
        const char *const *aszPre,
        const char * const *aszSucc, int nOrder)

以上函数原型中,pf为实际的钩子函数的指针,而其他三个参数主要用于钩子的排序(见下一小节),其意义如下:
aszPre  此钩子的前驱节点,也即此钩子必须排在哪个钩子之后
aszSucc 此钩子的后驱节点,也即此钩子必须排在哪个钩子之前
nOrder  此钩子顺序值(也可以视作排序的优先级)

6. 钩子的排序
不同的模块可能对同一钩子挂载多个钩子函数,如何决定这些钩子函数被调用的先后顺序呢。
通过对apr_hooks.c中排序相关的代码分析,钩子函数的排序主要由以下规则决定:
1) Order——钩子函数排序时,首先会用快速排序法对钩子函数的数组进行一次排序,qsort使用的比较函数是crude_order:
static int crude_order(const void *a_,const void *b_)
{
const TSortData *a=a_;
const TSortData *b=b_;

return a->nOrder-b->nOrder;
}
也即Order越小的钩子函数排在越前面。系统定义的Order有这几种:
/** run this hook first, before ANYTHING */
#define APR_HOOK_REALLY_FIRST (-10)
/** run this hook first */
#define APR_HOOK_FIRST  0
/** run this hook somewhere */
#define APR_HOOK_MIDDLE  10
/** run this hook after every other hook which is defined*/
#define APR_HOOK_LAST  20
/** run this hook last, after EVERYTHING */
#define APR_HOOK_REALLY_LAST 30

2) 前驱和后驱——完成基于Order的排序后,还会根据钩子函数相关的前驱(Predecessors)和后驱(Successors)进行调整。此算法的流程如下:
a) 通过遍历钩子数组,计算每个钩子有多少个前驱(见apr_hooks.c中的prepare函数)
b) 初始化一个链表
c) 根据每个钩子的前驱数量,确定其在链表中的位置(见apr_hooks.c中的tsort函数)
d) 将钩子插入链表
e) 遍历链表,生成一个新的钩子数组

此算法的流程稍显复杂,在Apache中也使用很少。大多数地方都只使用Order来决定钩子的顺序。

此外,APR提供了一个单独的函数apr_hook_sort_all来对系统内所有的Hook进行排序。这带来了一个问题:_hooks是静态函数,那apr_hook_sort_all是怎么获得系统内的所有Hooks呢?

解答就在钩子的挂载函数中——注意ap_hook_log_transaction中的这几句代码:
if(!_hooks.link_log_transaction)
{
  _hooks.link_log_transaction=apr_array_make(apr_hook_global_pool,
                       1,
                       sizeof(ap_AP_log_transaction_t));
  apr_hook_sort_register(log_transaction,&_hooks.link_log_transaction);
}

在创建一个新的钩子数组之后,调用了apr_hook_sort_register函数,此函数将新建的钩子数组注册到一个全局表中去。这样apr_hook_sort_all就可以对所有的钩子数组进行排序了。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-324614-1-1.html 上篇帖子: 10分钟教会你Apache Shiro 下篇帖子: org.apache.commons包介绍及应用
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表