tingya | 18 一月, 2005 22:48
该文章分析了Apache中的配置命令执行过程
/////////////////////////////////////////////////////////////
//Apache源代码分析——配置命令执行过程
//张中庆于西安交通大学软件所
//tingya$stu,xjtu,edu,cn,将$换成@,,换成.防止地址被收集
//转载请保留出处
//最初出自西安交通大学兵马俑Linux版
//////////////////////////////////////////////////////////////
该部分是与前面的命令表的部分紧密关联的,因此阅读这部分请先阅读前面的命令表分析。
////////////////////////////////////////////////////////////////////////////
static const char *invoke_cmd(const command_rec *cmd, cmd_parms *parms,
void *mconfig, const char *args)
apache内部对命令的实现最终是通过invoke_cmd实现的,在invoke_cmd的基础之上进行了简单的包装,构成了execute_now函数。
函数首先检测该指令是否出现在它应该出现的地方。这个可以通过将通过AllowOverride设置的parms->override和cmd->req_override进行比较,如果相等,则表明合法,否则表明指令不应该出现在这个位置。
函数最终将根据cmd->args_how来进行实际的函数调用,args_how指明了调用函数的实际的原型。首先我们看一下最简单的args_how为RAW_ARGS的情况,简化后函数的相关执行语句如下:
switch (cmd->args_how) {
case RAW_ARGS:
return cmd->AP_RAW_ARGS(parms, mconfig, args);
……
}
在cmd中我们声明了执行函数为func,其类型为cmd_fun。在前面我们说过不同指令最终实际的函数原型是不一样,其函数的参数由args_how决定,那么Apache是如何作到仅用一个函数名就达到调用各种不同函数的目的的呢。在C++下这个倒是可以通过重载实现,而C中则没有直接的方法。为此Apache采取了一些变通的方法。我们看一下下面的定义就会明白实现策略了。
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 *(*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_func原来是一个联合类型,apache将所有可能用到的函数原型都放在里面的。因此需要的时候只需要从中去取就可以了。那么现在我们在看看宏AP_RAW_ARGS的实现:
# define AP_RAW_ARGS func.raw_args
原来AP_RAW_ARGS宏无非就是用来获取RAW_ARGS类型指令处理函数的名字,一旦得到名称就可以将参数传递进去,进行类似下面的函数调用:
cmd->AP_RAW_ARGS(parms, mconfig, args);
至此,函数开始执行指令对应的函数。所有的指令的调用都大同小异,因此我们不再对调用方式做过多的描述,我们下面来看看不同类型的指令apache是如何处理的。
(1),对于RAW_ARGS类型的指令,服务器不对其做任何修改,因此,传进来什么参数,服务器原封不动的再传给指令处理函数。
(2),NO_ARGS类型指令也很简单,服务器什么都不需要传入给执行函数。
(3),如果是TAKE1类型的指令,由于指令后面带一个参数,为此必须得到该参数,函数将调用ap_getword_conf得到该参数,然后将其传入处理函数。对于TAKE1指令,即使后面存在多个指令参数,处理器也只取第一个。
(4),如果是TAKE2或者TAKE3指令,处理方法与TAKE1指令类似,只是要调用两次或三次ap_getword_conf得到个个参数。如果指令后面所带的参数不能满足要求,函数将报错,返回错误信息。
(5),如果指令是TAKE12,TAKE13,TAKE23或者TAKE123,处理函数处理之前必须保证指令参数在规定范围之内。如果不足,相应参数设置为NULL。
(6),如果指令是ITERATE,那么指令的参数个数将不确定,为此处理器将循环迭代调用ap_getword_conf,每次取一个参数,然后调用TAKE1指令处理函数;处理完继续取下一个,处理代码如下:
while (*(w = ap_getword_conf(parms->pool, &args)) != '') {
errmsg = cmd->AP_TAKE1(parms, mconfig, w);
if (errmsg && strcmp(errmsg, DECLINE_CMD) != 0)
return errmsg;
}
(7),ITERATE2指令与ITERAT1不同,其对指令的迭代处理是从第二个参数开始,第一个指令不参与迭代。处理器取出第一个参数后,然后将它和第二个参数开始的每一个参数联合作为指令函数的两个处理参数,处理代码如下:
w = ap_getword_conf(parms->pool, &args);
……
while (*(w2 = ap_getword_conf(parms->pool, &args)) != '') {
errmsg = cmd->AP_TAKE2(parms, mconfig, w, w2);
……
}
(8),FLAG指令与前面的所有指令处理方法都不一样,该指令后面的参数只有两种:On和Off,其余的都是非法。如果为“On”,传递给处理函数的相应参数为1,否则为0,具体代码如下:
w = ap_getword_conf(parms->pool, &args);
if (*w == '' || (strcasecmp(w, "on") && strcasecmp(w, "off")))
return apr_pstrcat(parms->pool, cmd->name, " must be On or Off",NULL);
return cmd->AP_FLAG(parms, mconfig, strcasecmp(w, "off") != 0);