给每个模块排序是依靠:
1
2
3
4
5
|
ngx_max_module = 0;
for (i = 0; ngx_modules[i]; i++)
{
ngx_modules[i]->index = ngx_max_module++;
}
|
nginx中最难的也是最重要的函数就是
里面做了大量的初始化操作,之前每次看nginx源码都卡在这个函数,郁闷。
硬着头皮慢慢看。
然后在初始化的操作中,先是执行下列代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
for (i = 0; ngx_modules[i]; i++)
{
if (ngx_modules[i]->type != NGX_CORE_MODULE)
{
continue;
}
module = ngx_modules[i]->ctx;
if (module->create_conf)
{
rv = module->create_conf(cycle);
if (rv == NGX_CONF_ERROR) {
ngx_destroy_pool(pool);
return NULL;
}
cycle->conf_ctx[ngx_modules[i]->index] = rv;
}
}
|
也就是说,对于N个模块来说,对于满足1)CORE_MODULE 2)定义了create_conf函数,
那么究竟哪些模块执行了create_conf函数呢,如下所示:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
1)ngx_core_module ,执行 ngx_core_module_create_conf 函数。
这个函数返回的缓冲区内容为:
{
ngx_flag_t daemon;
-1
ngx_flag_t master;
-1
ngx_int_t worker_processes;
-1
ngx_uid_t user;
-1
ngx_gid_t group;
-1
ngx_str_t pid;
ngx_str_t newpid;
#if (NGX_THREADS)
ngx_int_t worker_threads;
size_t thread_stack_size;
#endif
}
|
通过gdb的跟踪,也就只有第一个模块执行了create_conf函数。(无语)
执行相应的函数,结果作为对应模块的一个配置项存储区。
~~~~~~~~然后执行 ngx_conf_parse 函数
这个函数也是比较难的,从名字来看,显然是解析配置文件,
但是这个解析的过程也是千回百转,不把人绕死不死心的感觉,继续硬着头皮。。。
先打开配置文件,然后通过ngx_conf_read_token来读取。
先看看配置文件里有啥:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
user nobody;
worker_processes 3;
#error_log logs/error.log;
#pid logs/nginx.pid;
events {
connections 1024;
}
http {
include conf/mime.types;
default_type application/octet-stream;
sendfile on;
#gzip on;
server {
listen 80;
charset on;
source_charset koi8-r;
#access_log logs/access.log;
location / {
root html;
index index.html index.htm;
}
}
}
|
那么这个配置文件究竟是如何解析的呢?
第一个字符是\n,忽略
第2行:user nobody;---是通过下面的函数来执行的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
for (m = 0; rc != NGX_ERROR && !found && ngx_modules[m]; m++)
{
/* look up the directive in the appropriate modules */
if (ngx_modules[m]->type != NGX_CONF_MODULE
&& ngx_modules[m]->type != cf->module_type)
{
continue;
}
cmd = ngx_modules[m]->commands;
if (cmd == NULL) {
continue;
}
while (cmd->name.len) {
if (name->len == cmd->name.len
&& ngx_strcmp(name->data, cmd->name.data) == 0)
{
found = 1;
#if 0
ngx_log_debug(cf->log, "command '%s'" _ cmd->name.data);
#endif
/* is the directive's location right ? */
if ((cmd->type & cf->cmd_type) == 0) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"directive \"%s\" in %s:%d "
"is not allowed here",
name->data,
cf->conf_file->file.name.data,
cf->conf_file->line);
rc = NGX_ERROR;
break;
}
/* is the directive's argument count right ? */
if (cmd->type & NGX_CONF_ANY) {
valid = 1;
} else if (cmd->type & NGX_CONF_FLAG) {
if (cf->args->nelts == 2) {
valid = 1;
} else {
valid = 0;
}
} else if (cmd->type & NGX_CONF_1MORE) {
if (cf->args->nelts > 1) {
valid = 1;
} else {
valid = 0;
}
} else if (cmd->type & NGX_CONF_2MORE) {
if (cf->args->nelts > 2) {
valid = 1;
} else {
valid = 0;
}
} else if (cf->args->nelts <= 10
&& (cmd->type
& argument_number[cf->args->nelts - 1]))
{
valid = 1;
} else {
valid = 0;
}
if (!valid) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"invalid number arguments in "
"directive \"%s\" in %s:%d",
name->data,
cf->conf_file->file.name.data,
cf->conf_file->line);
rc = NGX_ERROR;
break;
}
/* set up the directive's configuration context */
conf = NULL;
if (cmd->type & NGX_DIRECT_CONF) {
conf = ((void **) cf->ctx)[ngx_modules[m]->index];
} else if (cmd->type & NGX_MAIN_CONF) {
conf = &(((void **) cf->ctx)[ngx_modules[m]->index]);
} else if (cf->ctx) {
confp = *(void **) ((char *) cf->ctx + cmd->conf);
if (confp) {
conf = confp[ngx_modules[m]->ctx_index];
}
}
rv = cmd->set(cf, cmd, conf);
#if 0
ngx_log_debug(cf->log, "rv: %d" _ rv);
#endif
if (rv == NGX_CONF_OK) {
break;
} else if (rv == NGX_CONF_ERROR) {
rc = NGX_ERROR;
break;
} else {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"the \"%s\" directive %s in %s:%d",
name->data, rv,
cf->conf_file->file.name.data,
cf->conf_file->line);
rc = NGX_ERROR;
break;
}
}
cmd++;
}
}
|
也就是说,模块要么为CONF模块,要么为默认的NGX_CORE_MODULE,
如果当前模块符合条件,则对这个模块的命令列表进行过滤。
对于每一个命令来说
1)要求名字相同
2)命令类型满足当前需求
3)命令的参数数目正确
对于NGX_MAIN_CONF类型的命令,通过
1
|
conf = &(((void **) cf->ctx)[ngx_modules[m]->index]);
|
找到配置项缓冲区。
然后执行:
1
|
rv = cmd->set(cf, cmd, conf);
|
对于配置项 user来说,执行;ngx_set_user函数。
~~~~~~~~~~~~~~
对于配置项worker_processes 3;,同样的道理,很简单,执行:ngx_conf_set_num_slot
~~~~~~~~~~~~~~~~~~~~~~
#error_log logs/error.log;
#pid logs/nginx.pid;
剩下的2行注释了,我也就不细说了,道理是一样的。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
当读取到events {时,返回NGX_OK,然后执行
这个函数到底做了什么?
1
2
3
4
5
6
7
8
|
ngx_event_max_module = 0;
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_EVENT_MODULE) {
continue;
}
ngx_modules[m]->ctx_index = ngx_event_max_module++;
}
|
可见,先给NGX_EVENT_MODULE模块进行内部编号,ctx_index意味着是相对的上下文编号。
然后执行下面的函数。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
for (m = 0; ngx_modules[m]; m++)
{
if (ngx_modules[m]->type != NGX_EVENT_MODULE)
{
continue;
}
module = ngx_modules[m]->ctx;
if (module->create_conf)
{
ngx_test_null((*ctx)[ngx_modules[m]->ctx_index],
module->create_conf(cf->cycle),
NGX_CONF_ERROR);
}
}
|
之前说了,event有3个模块,每个模块都有自己的create_conf函数:
其实也就是依次执行了
准备工作做好之后,又执行rv = ngx_conf_parse(cf, NULL);函数
这是为了解析{}里的内容,现在看看bt
1
2
3
4
5
|
#0 ngx_conf_parse (cf=0xbffff3cc, filename=0x0) at src/core/ngx_conf_file.c:65
#1 0x080532ff in ngx_events_block (cf=0xbffff3cc, cmd=0x808b5a0, conf=0x8097ae8) at src/event/ngx_event.c:509
#2 0x08050f88 in ngx_conf_parse (cf=0xbffff3cc, filename=0x8097684) at src/core/ngx_conf_file.c:253
#3 0x0804f61d in ngx_init_cycle (old_cycle=0xbffff468) at src/core/ngx_cycle.c:162
#4 0x08049c33 in main (argc=3, argv=0xbffff594) at src/core/nginx.c:164
|
这里存在2个ngx_conf_parse,可别被绕晕了,注意第2层的ngx_conf_parse的第2个参数为NULL.
继续解析配置文件项:
1
|
connections 1024;对应的解析函数是:ngx_event_connections
|
然后读到},ngx_conf_read_token返回NGX_CONF_BLOCK_DONE。
至此,event模块就解析完毕,然后即使这里面添加别的参数配置,道理都是一样的,还是不难理解的。
配置文件还剩下以下内容未解析:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
http {
include conf/mime.types;
default_type application/octet-stream;
sendfile on;
#gzip on;
server {
listen 80;
charset on;
source_charset koi8-r;
#access_log logs/access.log;
location / {
root html;
index index.html index.htm;
}
}
}
|
择日再进行更新,先睡觉。
不定时更新!
~~~~~~~~~~~继续更新
话说上回说到nginx的event模块的配置项解析完毕,然后nginx执行下面的代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_EVENT_MODULE) {
continue;
}
module = ngx_modules[m]->ctx;
if (module->init_conf) {
rv = module->init_conf(cf->cycle,
(*ctx)[ngx_modules[m]->ctx_index]);
if (rv != NGX_CONF_OK) {
return rv;
}
}
}
|
实际上就是说,对于所有模块,如果是event模块,并且定义了init_conf函数,则执行这个函数进行初始化操作。
我们知道定义了3个event模块,每个模块之前也分配了配置项缓冲区,其实就是连续执行了以下几个函数,用来初始化缓冲区,总结起来流程就是:1)create创建缓冲区 2)尽可能读配置项给缓冲区赋值 3)为了保证系统正常运行,执行初始化操作,下面就是3个模块的各个初始化函数。
这个函数执行完,对应缓冲区的内容是:
1
2
|
(gdb) p *ecf
$1 = {connections = 1024, use = 2, multi_accept = 0, accept_mutex = 1, accept_mutex_delay = 500, name = 0x439b20 "epoll"}
|
然后执行函数:ngx_rtsig_init_conf
这个函数执行完,对应的缓冲区内容是:
1
2
|
(gdb) p *rtscf
$5 = {signo = 44, overflow_events = 16, overflow_test = 32, overflow_threshold = 10}
|
然后执行最后一个函数:ngx_epoll_init_conf
执行完,对应的缓冲区内容是:
1
2
|
(gdb) p *epcf
$6 = {events = 512}
|
那么只剩下http的配置项了,
继续执行 ngx_conf_read_token函数。
读取http {,函数返回,查看参数缓冲区内容:
1
2
|
(gdb) p name[0]
$8 = {len = 4, data = 0x6547d0 "http"}
|
表明确实读到了http block的配置项。
然后接着查找相应的模块和命令来处理。
注意此时模块的要求仍然是:NGX_CONF_MODULE或者NGX_CORE_MODULE,
因为此时的http模块刚开始解析时,仍然是作为core模块。
(当然,后面肯定有一个函数会把后续配置项的对应模块设置为NGX_HTTP_MODULE,这个后面再说。)
代码继续执行,http对应的执行函数是:
这个函数先执行
1
2
3
4
5
6
7
8
|
ngx_http_max_module = 0;
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
continue;
}
ngx_modules[m]->ctx_index = ngx_http_max_module++;
}
|
啥都不说,先内部排号再说。
接下来就是执行下面的代码:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
for (m = 0; ngx_modules[m]; m++)
{
if (ngx_modules[m]->type != NGX_HTTP_MODULE)
{
continue;
}
module = ngx_modules[m]->ctx;
mi = ngx_modules[m]->ctx_index;
if (module->pre_conf)
{
if (module->pre_conf(cf) != NGX_OK)
{
return NGX_CONF_ERROR;
}
}
if (module->create_main_conf)
{
ngx_test_null(ctx->main_conf[mi], module->create_main_conf(cf),
NGX_CONF_ERROR);
}
if (module->create_srv_conf)
{
ngx_test_null(ctx->srv_conf[mi], module->create_srv_conf(cf),
NGX_CONF_ERROR);
}
if (module->create_loc_conf)
{
ngx_test_null(ctx->loc_conf[mi], module->create_loc_conf(cf),
NGX_CONF_ERROR);
}
}
|
也就是说,针对每个NGX_HTTP_MODULE,
如果定义了上面的4种函数,依次执行。根据GDB跟踪,依次执行了下列函数:
1
|
ngx_http_core_create_main_conf
|
1
|
ngx_http_core_create_srv_conf
|
1
|
ngx_http_core_create_loc_conf
|
~~~
1
|
ngx_http_log_create_main_conf<span></span>
|
1
|
ngx_http_log_create_loc_conf
|
~~~
?
1
|
ngx_http_static_create_loc_conf
|
~~~
1
|
ngx_http_index_create_loc_conf
|
~~~
1
|
ngx_http_access_create_loc_conf
|
~~~
1
|
ngx_http_rewrite_create_srv_conf
|
?
1
|
ngx_http_rewrite_create_loc_conf
|
~~~
1
2
|
ngx_http_proxy_pre_conf
ngx_http_proxy_create_loc_conf
|
~~~~~~
1
2
|
ngx_http_gzip_pre_conf
ngx_http_gzip_create_conf
|
~~~
1
2
|
ngx_http_charset_create_main_conf
ngx_http_charset_create_loc_conf
|
~~~
1
2
|
ngx_http_userid_pre_conf
ngx_http_userid_create_conf
|
~~~
1
|
ngx_http_headers_create_conf
|
~~~
1
|
ngx_http_copy_filter_create_conf
|
好,既然已经准备工作做好,在解析http模块前
需要设置好模块类型和命令类型:
1
2
|
cf->module_type = NGX_HTTP_MODULE;
cf->cmd_type = NGX_HTTP_MAIN_CONF;
|
然后继续执行 rv = ngx_conf_parse(cf, NULL);来解析剩下的配置项
先读取include conf/mime.types;
查看gdb
?
1
2
3
4
|
(gdb) p name[0]
$3 = {len = 7, data = 0x6556d0 "include"}
(gdb) p name[1]
$4 = {len = 15, data = 0x6556e0 "conf/mime.types"}
|
处理函数为ngx_conf_include
先看看对应的conf/mime.types的内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
types {
text/html html htm shtml;
text/xml xml rss;
text/css css;
text/plain txt;
image/gif gif;
image/png png;
image/jpeg jpeg jpg;
image/x-icon ico;
application/pdf pdf;
application/x-shockwave-flash swf;
application/x-javascript js;
audio/mpeg mp3;
audio/x-realaudio ra;
video/mpeg mpeg mpg;
video/quicktime mov;
video/x-msvideo avi;
video/x-ms-wmv wmv;
}
|
对应的执行函数:ngx_types_block
然后解析default_type application/octet-stream;对应的函数:
然后解析sendfile on;
对应的处理函数:
这个函数的作用主要是如下:
1
2
3
4
5
6
7
|
if (ngx_strcasecmp(value[1].data, "on") == 0) {
*fp = 1;
} else if (ngx_strcasecmp(value[1].data, "off") == 0) {
*fp = 0;
}
|
接下来就是解析到了server {这个。
1
2
|
(gdb) p name[0]
$27 = {len = 6, data = 0x6568c8 "server"}
|
看相应的命令如下:
1
2
3
4
5
6
|
{ ngx_string("server"),
NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
ngx_server_block,
0,
0,
NULL },
|
则实际上是对main_conf[0]进行赋值操作。
好,相应的执行函数:ngx_server_block
在这个函数里,又重新分配了main_conf,serv_conf,loc_conf,(真是折腾),
然后通过下面的代码继续执行:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
for (m = 0; ngx_modules[m]; m++)
{
if (ngx_modules[m]->type != NGX_HTTP_MODULE)
{
continue;
}
module = ngx_modules[m]->ctx;
if (module->create_srv_conf)
{
ngx_test_null(ctx->srv_conf[ngx_modules[m]->ctx_index],
module->create_srv_conf(cf),
NGX_CONF_ERROR);
}
if (module->create_loc_conf)
{
ngx_test_null(ctx->loc_conf[ngx_modules[m]->ctx_index],
module->create_loc_conf(cf),
NGX_CONF_ERROR);
}
}
|
通过跟踪,陆续执行了以下几个函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
ngx_http_core_create_srv_conf
ngx_http_core_create_loc_conf
~~~
ngx_http_log_create_loc_conf
~~~
ngx_http_static_create_loc_conf
~~~
ngx_http_index_create_loc_conf
~~~
ngx_http_access_create_loc_conf
~~~
ngx_http_rewrite_create_srv_conf
ngx_http_rewrite_create_loc_conf
~~~
ngx_http_proxy_create_loc_conf
~~~
ngx_http_gzip_create_conf
~~~
ngx_http_charset_create_loc_conf
~~~
ngx_http_userid_create_conf
~~~
|
1
2
|
ngx_http_headers_create_conf
ngx_http_copy_filter_create_conf
|
好,接着往下执行,看看目前的bt状态
1
2
3
4
5
6
7
|
#0 ngx_conf_parse (cf=0xbffff3cc, filename=0x0) at src/core/ngx_conf_file.c:65
#1 0x08060c24 in ngx_server_block (cf=0xbffff3cc, cmd=0x808bb60, dummy=0x8098200) at src/http/ngx_http_core_module.c:965
#2 0x08050f88 in ngx_conf_parse (cf=0xbffff3cc, filename=0x0) at src/core/ngx_conf_file.c:253
#3 0x0805e9f5 in ngx_http_block (cf=0xbffff3cc, cmd=0x808ba80, conf=0x8097af8) at src/http/ngx_http.c:154
#4 0x08050f88 in ngx_conf_parse (cf=0xbffff3cc, filename=0x8097684) at src/core/ngx_conf_file.c:253
#5 0x0804f61d in ngx_init_cycle (old_cycle=0xbffff468) at src/core/ngx_cycle.c:162
#6 0x08049c33 in main (argc=3, argv=0xbffff594) at src/core/nginx.c:164
|
数一下有多少个ngx_conf_parse出现,。。汗,写代码容易,看代码累死人啊。
现在是读取listen 80;
1
2
3
4
|
(gdb) p name[0]
$4 = {len = 6, data = 0x8099a28 "listen"}
(gdb) p name[1]
$5 = {len = 2, data = 0x8099a30 "80"}
|
对应的函数为:ngx_set_listen
接下来读取配置charset on;
1
2
3
4
|
(gdb) p name[0]
$15 = {len = 7, data = 0x8099a34 "charset"}
(gdb) p name[1]
$16 = {len = 2, data = 0x8099a40 "on"}
|
对应的函数为:ngx_conf_set_flag_slot
接下来读取source_charset koi8-r;
1
2
3
4
|
(gdb) p name[0]
$20 = {len = 14, data = 0x8099a44 "source_charset"}
(gdb) p name[1]
$21 = {len = 6, data = 0x8099a54 "koi8-r"}
|
对应的函数 是:ngx_http_set_charset_slot
~~~~~~~~~~~~~~
(责任编辑:IT) |