Nginx源码分析模块指令加载
上文写到了Nginx配置文件读取以及解析,本文介绍模块的指令加载。还是以前文的nginx_move_domain_cache为例
server { listen 80; server_name www.firefoxbug.net; gzip on; location /mv_cache{ mv_cache; } .... }
上面有一模块mv_cache,这个模块里面的指令是mv_cache,参数是0个。再比如server_name也是一个指令,参数是一个"www.firefoxbug.net"。调用前文的ngx_conf_parse读取配置文件,然后调用handler函数进行解析,对于普通的处理(普通指令),会调用ngx_conf_handler,这里再回顾下模块的指令数组定义结构
typedef struct ngx_command_s { ngx_str_t name; //指令名字 ngx_uint_t type; //指令所属模块类型,预设参数,指令出现的位置等 char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); //指令处理函数,存储到相应的模块结构体 ngx_uint_t conf; //去内存里面找相应的模块,一般都是NGX_HTTP_MODULE ngx_uint_t offset;//该配置项的精确存放位置,一般都是偏移。用offsetof(A, b)宏来表示A结构体中的b偏移量 void *post; }ngx_command_t;
对应mv_cache模块的指令数组定义:
/* Commands */ static ngx_command_t ngx_http_move_domain_cache_commands[] = { { ngx_string("mv_cache"), NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS, ngx_http_move_domain_cache, 0, 0, NULL }, ngx_null_command };
下面看普通指令处理函数
src/core/ngx_conf_file.c -->> ngx_conf_handler() static ngx_int_t ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last) { // 获取指令的名字,这里是读取配置文字得到的 name = cf->args->elts; // 遍历每个模块,检查指令(比如这里就是nginx_move_domain_cache)是在哪个模块中被定义,并且进行处理 for (i = 0; ngx_modules[i]; i++) { /* look up the directive in the appropriate modules */ if (ngx_modules[i]->type != NGX_CONF_MODULE && ngx_modules[i]->type != cf->module_type) { continue; } //指令必须以NULL结尾的原因 cmd = ngx_modules[i]->commands; if (cmd == NULL) { continue; } //每个模块command数组 //遍历模块的command,比较名字,然后调用回调函数set for ( /* void */ ; cmd->name.len; cmd++) { // 这里先比较name(配置文件中的命令)和cmd->name(模块中定义的命令)的长度,再比较内容 if (name->len != cmd->name.len) { continue; } if (ngx_strcmp(name->data, cmd->name.data) != 0) { continue; } /* is the directive's location right ? */ //类型是否匹配 if (!(cmd->type & cf->cmd_type)) { if (cmd->type & NGX_CONF_MULTI) { multi = 1; continue; } goto not_allowed; } /* is the directive's argument count right ? */ if (!(cmd->type & NGX_CONF_ANY)) { if (cmd->type & NGX_CONF_FLAG) { if (cf->args->nelts != 2) { goto invalid; } } else if (cmd->type & NGX_CONF_1MORE) { if (cf->args->nelts < 2) { goto invalid; } } else if (cmd->type & NGX_CONF_2MORE) { if (cf->args->nelts < 3) { goto invalid; } } else if (cf->args->nelts > NGX_CONF_MAX_ARGS) { goto invalid; } else if (!(cmd->type & argument_number[cf->args->nelts - 1])) { goto invalid; } } /* set up the directive's configuration context */ conf = NULL; //最核心的地方, if (cmd->type & NGX_DIRECT_CONF) { //ctx是包含了所有core模块的conf(create_conf回调),因此这里取出对应的模块conf. conf = ((void **) cf->ctx)[ngx_modules[i]->index]; } else if (cmd->type & NGX_MAIN_CONF) { //如果不是DIRECT_CONF并且是MAIN,则说明我们需要在配置中创建自己模块的上下文(也就是需要进入二级模块) conf = &(((void **) cf->ctx)[ngx_modules[i]->index]); } else if (cf->ctx) { //否则进入二级模块处理 confp = *(void **) ((char *) cf->ctx + cmd->conf); if (confp) { conf = confp[ngx_modules[i]->ctx_index]; } } //调用对应命令的处理函数,这个回调函数根据 ngx_command_s 预设的,可以是自定义的参数处理函数,也可是调用Nginx提供的。 //比如worker_process这命令在nginx.c中对应的处理函数是ngx_conf_set_num_slot,这里的set就是ngx_conf_set_num_slot //这里还可以通过这个函数把本模块注册到NGX_HTTP_CONTENT_PHASE阶段 rv = cmd->set(cf, cmd, conf); } } }
对于具体的模块指令配置结构体存储,以后的文章再讨论。
上面就是指令的加载,这里做一个大概的总结:每一个模块定义的同时,会指定模块相应的指令数组,以及每个指令的处理函数。Nginx在初始化过程中,一旦读取到配置文件的指令配置(这里好像叫token)。就会去遍历相应模块,具体为从预先定义,编译的模块数组找出对应模块,然后根据模块的指令数组,一旦token长度和字符串和指令数组的某一项匹配,就会调用该指令的回调处理函数。通过这个函数可以1.处理相应的参数,保存到模块配置结构体里 ;2.把本模块注册到NGX_HTTP_CONTENT_PHASE阶段,这样,一旦请求匹配到对应的location,就调用模块处理。
评论已关闭