您的位置:首页 > 运维架构 > Nginx

nginx源码分析--nginx上下文结构初始化

2014-04-25 16:30 726 查看
说在前面:

本文的讨论基于的nginx版本是1.2.1。

文章对于nginx的一些背景知识(如什么是模块,上下文等)没有过多提及,本文适合对nginx有一定了解的同学。



ngx_conf_t *cf; 做过nginx模块开发,或者是有学习过nginx源码的同学会对这个结构体非常眼熟了。

ngx_cycle.c:251 conf.ctx = cycle->conf_ctx;



ctx(context)在nginx里随处可见,字面理解是上下文;在nginx里,ctx保存的每个模块的配置结构.

cycle->conf_ctx是一个四重指针,为什么需要四重指针?希望读完本文,会对你理解有一定帮助。





nginx配置文件nginx.conf(文章会围绕这个配置文件,分析nginx解析并保存模块上下文的过程)

1 http {
2     resolver 8.8.8.8;
3
4     server
5     {
6         listen 80;
7         server_name dcshi.com
8         root /data/html
9
10         location /a
11         {
12             set $foo $bar;
13
14             return 200;
15         }
16
17         location /b.html
18         {
19             root /opt/html;
20         }
21
22     }
23
24     server
25     {
26         listen 80;
27         server_name s2.dcshi.com
28
29         location /a {}
30
31         location /b {}
32     }
33 }



与上述配置文件对应的模块上下文(ctx)架构图:





1)ngx_cycle.c

215     for (i = 0; ngx_modules[i]; i++) {
216         if (ngx_modules[i]->type != NGX_CORE_MODULE) {
217             continue;
218         }
219
220         module = ngx_modules[i]->ctx;

/* 如果是核心模块,而且有定义了create_conf,则调用模块的create_conf来创建上下文结构 */
222         if (module->create_conf) {
223             rv = module->create_conf(cycle);
224             if (rv == NULL) {
225                 ngx_destroy_pool(pool);
226                 return NULL;
227             }
228             cycle->conf_ctx[ngx_modules[i]->index] = rv;
229         }
230     }



2)ngx_cycle.c

/*
* 调取ngx_conf_parse开始解析配置文件(nginx.conf),ngx_conf_parse是一个很通用的函数。
* 参数二是一个文件名,如果传入NULL,则解析当前的block配置。{ }配置文件里两花括号定义一个block。
* /
268     if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {
269         environ = senv;
270         ngx_destroy_cycle_pools(&conf);
271         return NULL;
272     }



3)ngx_conf_file.c:

/*
* 如果指令指定了handler函数,则直接调用
* cf->handler 什么时候不为NULL?后续再讨论。
*/
222         if (cf->handler) {
223
224             /*
225              * the custom handler, i.e., that is used in the http's
226              * "types { ... }" directive
227              */
228
229             rv = (*cf->handler)(cf, NULL, cf->handler_conf);
230             if (rv == NGX_CONF_OK) {
231                 continue;
232             }
233
234             if (rv == NGX_CONF_ERROR) {
235                 goto failed;
236             }
237
238             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, rv);
239
240             goto failed;
241         }
242
243
// cf->hanlder为NULL,则调用ngx_conf_handler来遍历所有模块的指令,以触发该指令对应的回调函数。
244         rc = ngx_conf_handler(cf, rc);



4)ngx_conf_file.c

/*
* NGX_DIRECT_CONF为什么是direct(直接),这类命令字只出现在core模块里
* direct字面理解是需要使用(赋值)时候可以直接拿来使用即可,即在调用前上下文结构已经分配好,如worker_processes指令
* 对应的非direct则需要先分配空间后才能使用,如http指令(http是一个指令,不是一类指令,不要搞混哦!)
* 上面提到的步骤一,nginx在解析配置文件之前,会依次调用NGX_CORE_MODULE 模块的create_conf,
* direct指令的上下文就是这个时候分配的.
*
*/
380             if (cmd->type & NGX_DIRECT_CONF) {
381                 conf = ((void **) cf->ctx)[ngx_modules[i]->index];
382
/*
* http指令就是一个NGX_MAIN_CONF指令(怎么确定指令type,请看ngx_command_s定义)
* 细心的同学看到,381行与384行的唯一区别是,后者是返回指针的地址。
* c/c++开发的同学都知道 func(char *p) 与 func(char **p)的区别。后者的func可以对传入的p重新分配内存
* 如http指令为例,http所在模块是ngx_http_module,其没有定义create_conf(ngx_http.c:98)
* 即在步骤一里是不会给ngx_http_module分配上下文结构的,所以需要在ngx_http_block里进行动态分配
* 所以,这就是为什么需要传入的是指针的地址。
* 至于为什么ngx_http_module不也提供create_conf回调,理论上结构更统一?答案留给你们自己思考。
*/
383             } else if (cmd->type & NGX_MAIN_CONF) {
384                 conf = &(((void **) cf->ctx)[ngx_modules[i]->index]);
385
386             } else if (cf->ctx) {
/*
* cmd->conf是什么?set指令的conf是 NGX_HTTP_LOC_CONF_OFFSET(ngx_http_rewrite_module.c:81)
* #define NGX_HTTP_LOC_CONF_OFFSET   offsetof(ngx_http_conf_ctx_t, loc_conf)
* 通过上面的宏定义,清楚可以知道cmd->conf返回的是一个变量在struct里的偏移
* nginx为每个http模块提供的上下文结构(ngx_http_conf_ctx_t)包括main_conf, srv_conf, loc_conf 三个元素;
* 对于一个指令而言,只会用到上下文结构中的一个*_conf;但对于一个模块(包含多个指令)而言,可能会用到任意多个*_conf。
* set指令可以运行在location和server,set指令的上下文结构又保存在srv_conf or loc_conf?请独立思考来验证自己是否已经对模块有一定理解。
*/
387                 confp = *(void **) ((char *) cf->ctx + cmd->conf);
388
389                 if (confp) {
390                     conf = confp[ngx_modules[i]->ctx_index];
391                 }
392             }
393
/* 调用指令对应的回调函数进行初始化操作 */
394             rv = cmd->set(cf, cmd, conf);



通过上面的代码片段,我们可以理解到,nginx是在边解析配置文件,边查找对应的模块指令,并调用指令绑定的回调函数进行初始化。

下面是http指令的定义:

83 static ngx_command_t  ngx_http_commands[] = {
84
85     { ngx_string("http"),
86       NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
87       ngx_http_block,
88       0,
89       0,
90       NULL },
91
92       ngx_null_command
93 };

ngx_http_block是一个很重要函数,当nginx在解析配置文件阶段,读取到http指令,即调用ngx_http_block函数对 http {….} 花括号里的配置进行解析。



5)ngx_http.c

120 ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
121 {
…...

130
131     /* the main http context */
132
133     ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
134     if (ctx == NULL) {
135         return NGX_CONF_ERROR;
136     }
137
/*

* 注意,传进来的conf被修改了。

* 因为http_module虽是core模块,但是并没有定义create_conf,所以步骤一并没有为http_module分配上下文空间

* http指令是NGX_MAIN_CONF,所以conf传入的实质是一个指针的指针

* 所以通过在这里重新赋值,来为conf分配空间

*/
138     *(ngx_http_conf_ctx_t **) conf = ctx;
139
140
141     /* 为所有的http module计算ctx_index */
142
143     ngx_http_max_module = 0;
144     for (m = 0; ngx_modules[m]; m++) {
145         if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
146             continue;
147         }
148
149         ngx_modules[m]->ctx_index = ngx_http_max_module++;
150     }
151
152
/*
* 为http ctx的的main_conf, srv_conf loc_conf 分配内存;
* 可以看到每个*_conf都是定长的,长度为ngx_http_max_module,
* 即每个模块都会被分配main_conf, srv_conf loc_conf,而不管改模块是否需要。
*/
155     ctx->main_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
…...
167     ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
…….
178     ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
/* 遍历所有的模块,分别调用其回调函数进行初始化 */
189     for (m = 0; ngx_modules[m]; m++) {
190         if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
191             continue;
192         }
193
194         module = ngx_modules[m]->ctx;
195         mi = ngx_modules[m]->ctx_index;
196
197         if (module->create_main_conf) {
198             ctx->main_conf[mi] = module->create_main_conf(cf);
202         }
203
204         if (module->create_srv_conf) {
205             ctx->srv_conf[mi] = module->create_srv_conf(cf);
209         }
210
211         if (module->create_loc_conf) {
212             ctx->loc_conf[mi] = module->create_loc_conf(cf);
216         }
217     }
218
/* 对cf进行一个临时拷贝,在329行重新恢复。 */
219     pcf = *cf;
/*
* 注意:改变cf->ctx的上下文;nginx在解析配置文件的过程中,cf->ctx总会临时修改(最后被恢复)。
* 在解析http {} 的时候cf->ctx表示http_module的上下文结构
* 在解析server{} 的时候cf->ctx表示的是server的上下文结构
* 在解析location{} 的时候,cf->ctx表示的是location的上下文结构
* 可结合文章开头的架构图理解
*/
220     cf->ctx = ctx;
…...
235
236     /* 解析http { }, 依次调用涉及的指令回调函数,server作为http里一个必须的指令,也是在这个时候被调用  */
238     cf->module_type = NGX_HTTP_MODULE;
239     cf->cmd_type = NGX_HTTP_MAIN_CONF;
240     rv = ngx_conf_parse(cf, NULL);
241
242     if (rv != NGX_CONF_OK) {
243         goto failed;
244     }
…..
328
329     *cf = pcf;



6)ngx_http_core_module.c

/*
* 注意:正如第三个参数的命名-dummy(假的),本函数里没有使用.
* 没有使用传入的conf,那http与server的ctx是怎样串联的?(答案在文章开篇的架构图)
*/
2832 ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
2833 {
……
/* 为server指令分配一个上下文,这个上文终究要跟http的上下文串联起来的 */
2845     ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
2846     if (ctx == NULL) {
2847         return NGX_CONF_ERROR;
2848     }
/*
* 注意:server的ctx->main_conf是直接指向http_ctx(http的ctx,比server ctx更高一级)的main_conf
* 与ngx_http_block不同,这里只对上下文结构里的srv_conf和loc_conf进行内存分配
* 原因很简单,在server里调用的指令不可能用到main_conf的配置结构.
* 相同的道理,location里的指令不可能用到main_conf和srv_conf,所以只需要对loc_conf内存即可(ngx_http_core_location)
* 建议结合文章开篇的架构图理解
*/
2850     http_ctx = cf->ctx;
2851     ctx->main_conf = http_ctx->main_conf;
2852
…….
2855     ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
…….
2862     ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
/* 遍历所有的http module,对分别调用create_srv_conf 和 create_loc_conf进行初始化上下文配置 */
2867     for (i = 0; ngx_modules[i]; i++) {
2868         if (ngx_modules[i]->type != NGX_HTTP_MODULE) {
2869             continue;
2870         }
2871
2872         module = ngx_modules[i]->ctx;
2873
2874         if (module->create_srv_conf) {
2875             mconf = module->create_srv_conf(cf);
2880             ctx->srv_conf[ngx_modules[i]->ctx_index] = mconf;
2881         }
2882
2883         if (module->create_loc_conf) {
2884             mconf = module->create_loc_conf(cf);
2889             ctx->loc_conf[ngx_modules[i]->ctx_index] = mconf;
2890         }
2891     }
/*
* 核心来了,这段代码解释了http_ctx与srv_ctx怎样串联
* 2896, 2897行目的是把整个srv_ctx 先保存在ngx_http_core_module模块的ctx变量里
* 结合架构图理解
*/
2896     cscf = ctx->srv_conf[ngx_http_core_module.ctx_index];
2897     cscf->ctx = ctx;
2898
/*
* 串联了,http_ctx->main_conf(与srv_ctx->main_conf是等价的,还记得2851行吧?)
* 2900 - 2907行展示了串联的逻辑:整个ctx并没有直接串联,实际上是通过ngx_http_core_module模块进行串联的
* cmcf->servers是一个array类型,所以nginx.conf里配置的多个server也被依次串联起来
* 结合架构图理解
* /
2900     cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];
2902     cscfp = ngx_array_push(&cmcf->servers);
2903     if (cscfp == NULL) {
2904         return NGX_CONF_ERROR;
2905     }
2906
2907     *cscfp = cscf;
2908
/* 继续调用ngx_conf_parse对server{ } 进行解析,其中location作为server里的一个指令,也在这个时候被调用。见步骤7 */
2911
2912     pcf = *cf;
2913     cf->ctx = ctx;
2914     cf->cmd_type = NGX_HTTP_SRV_CONF;
2915
2916     rv = ngx_conf_parse(cf, NULL);
2917

/* cf被恢复 */
2918     *cf = pcf;



7) ngx_http_core_module.c

/*
* 注意:正如第三个参数的命名-dummy(假的),本函数里没有使用.
*/
2956 ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
2957 {
…….
2968     ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
2969     if (ctx == NULL) {
2970         return NGX_CONF_ERROR;
2971     }
2972
/* 正如上文所说,location里的指令不可能使用main_conf和srv_conf,所以只需要为loc_conf分配空间 */
2973     pctx = cf->ctx;
2974     ctx->main_conf = pctx->main_conf;
2975     ctx->srv_conf = pctx->srv_conf;
2976
2977     ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
/* 变量所有http module,依次调用create_loc_conf进行上下文初始化操作 */
2982     for (i = 0; ngx_modules[i]; i++) {
2983         if (ngx_modules[i]->type != NGX_HTTP_MODULE) {
2984             continue;
2985         }

2987         module = ngx_modules[i]->ctx;
2988
2989         if (module->create_loc_conf) {
2990             ctx->loc_conf[ngx_modules[i]->ctx_index] = module->create_loc_conf(cf);
2996     }
2997
/*
* 2998 - 3084 与步骤6类似,通过ngx_http_core_module 进行串联
* 结合架构图理解
*/
2998     clcf = ctx->loc_conf[ngx_http_core_module.ctx_index];
2999     clcf->loc_conf = ctx->loc_conf;
3000
…….
3083     pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index];
3084
3085     if (pclcf->name.len) {
……. //location是允许嵌套的,如location /a { location /b { } } ,暂不讨论
3131     }
3132
3133     if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) {
3134         return NGX_CONF_ERROR;
3135     }
/*  继续对location里的指令进行解析;值得注意的是,cf->ctx被修改,指向的是当前location的ctx */

3137     save = *cf;
3138     cf->ctx = ctx;
3139     cf->cmd_type = NGX_HTTP_LOC_CONF;
3140
3141     rv = ngx_conf_parse(cf, NULL);
3142
3143     *cf = save;
3144
3145     return rv;
3146 }



8)ngx_http.c

步骤5, http对应的回调函数是ngx_http_block.在240行完成对整个http { } block的解析以后,nginx还需要合并配置。

从上面的分析,提及到的ctx有http_ctx, srv_ctx, loc_ctx;以loc_conf为例,三个ctx都有包含,而且是互相独立的。

如 root 指令既可以定义在server { } ,也可以定义在location { };

如果location里没有对root进行定义,则location的loc_conf需要继承server { } 的root配置,这就是merge的作用。

下面的代码则展示了,nginx是如何进行merge:

分别 merge http_ctx 和 srv_ctx的 srv_conf 和 loc_conf
merge srv_ctx 和 loc_ctx的loc_conf

251     cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];
252     cscfp = cmcf->servers.elts;
253
254     for (m = 0; ngx_modules[m]; m++) {
255         if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
256             continue;
257         }
258
259         module = ngx_modules[m]->ctx;
260         mi = ngx_modules[m]->ctx_index;
261
262         /* init http{} main_conf's */
263
264         if (module->init_main_conf) {
265             rv = module->init_main_conf(cf, ctx->main_conf[mi]);
266             if (rv != NGX_CONF_OK) {
267                 goto failed;
268             }
269         }
270
271         rv = ngx_http_merge_servers(cf, cmcf, module, mi);
272         if (rv != NGX_CONF_OK) {
273             goto failed;
274         }
275     }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: