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

NGINX----源码阅读---配置文件解析(由简到杂)

2017-02-28 22:43 645 查看
我们知道nginx的配置是从配置文件中读取的,那么nginx是怎么将这些配置文件对应的配置项和值填入对应模块的内存的。下面我们将从简单的配置解析逐渐到整个配置的解析进行。

打开nginx.conf可以看到配置文件中有简单配置和复杂配置,我们这里先解析只有简单的配置,因此对配置解析的代码进行精简,只提取有用的部分。

1.配置文件nginx.conf,且配置文件中只有

worker_processes 1;

nginx的main函数在nginx.c文件中,关于配置的开始可以理解在ngx_init_cycle函数中调用所有模块的create_conf钩子这里,不过这节我们不关注这个。后边的代码就是如何将配置文件的配置解析到对应的模块里边的。

入口函数为ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename)

ngx_conf_parse函数的入参cf理解为配置的内存存储,存放了配置名称、参数等,filename就是将要解析的配置文件名。

首先解析nginx.conf配置,需要进行文件读入操作。

以下代码主要的工作是:

1)打开配置文件nginx.conf;

2)初始化conf cf,存储配置文件的基本信息(文件名、文件读取位置、读取文件的缓存等)关于配置文件相关的结构体可以查看:。。。。。

1 u_char      *start, ch, *src, *dst;                                 //start 缓存的开始  ch当前读取的字符
2 off_t        file_size;                                             //配置文件的大小
3 size_t       len;                                                   //一次读取的字符数
4 ssize_t      n, size;                                               //
5 ngx_uint_t   found, need_space, last_space, sharp_comment, variable;//found是否找到token   needspace是否使用空格  last_space开始  sharp_comment评论标示
6 ngx_uint_t   quoted, s_quoted, d_quoted, start_line;                //quoted是/的标示  s_quoted是'的标示  d_quoted"的标示  start_line
7 ngx_str_t   *word;
8 ngx_buf_t   *b, *dump;
9
10 /*初始化上边的基础变量*/
11 found = 0;
12 need_space = 0;
13 last_space = 1;
14 sharp_comment = 0;
15 variable = 0;
16 quoted = 0;
17 s_quoted = 0;
18 d_quoted = 0;
19
20 //配置文件的参数个数
21 cf->args->nelts = 0;
22 //配置文件的缓存,用于读取配置文件暂存字符的地方
23 b = cf->conf_file->buffer;
24 dump = cf->conf_file->dump;
25 //缓存起始地址
26 start = b->pos;
27 //配置文件的起始行
28 start_line = cf->conf_file->line;
29
30 //配置文件的大小
31 file_size = ngx_file_size(&cf->conf_file->file.info);


View Code
配置文件的字符解析

for ( ;; ) {
//如果当前缓存已经读完,则需要从文件中继续读入数据
if (b->pos >= b->last) {
//表示当前文件已经读取完毕,但是需要进行最后的校验,如果参数还有未解析的或者开始解析为1报错
if (cf->conf_file->file.offset >= file_size) {

if (cf->args->nelts > 0 || !last_space) {
//配置项后缺少分号
if (cf->conf_file->file.fd == NGX_INVALID_FILE) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unexpected end of parameter, "
"expecting \";\"");
return NGX_ERROR;
}

ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unexpected end of file, "
"expecting \";\" or \"}\"");
return NGX_ERROR;
}
//配置文件解析完成
return NGX_CONF_FILE_DONE;
}
//表示当前已经解析的缓存字符长度
len = b->pos - start;
//表示缓存中的字符解析完成??????没搞明白
if (len == NGX_CONF_BUFFER) {
cf->conf_file->line = start_line;

if (d_quoted) {
ch = '"';

} else if (s_quoted) {
ch = '\'';

} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"too long parameter \"%*s...\" started",
10, start);
return NGX_ERROR;
}

ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"too long parameter, probably "
"missing terminating \"%c\" character", ch);
return NGX_ERROR;
}
//将start后的len字符移到缓存最前边
if (len) {
ngx_memmove(b->start, start, len);
}
//size表示文件还没有被读的部分大小
size = (ssize_t) (file_size - cf->conf_file->file.offset);
//取剩余文件字符数和剩余缓存数的最小值,作为读取文件的字符数
if (size > b->end - (b->start + len)) {
size = b->end - (b->start + len);
}

n = ngx_read_file(&cf->conf_file->file, b->start + len, size,
cf->conf_file->file.offset);

if (n == NGX_ERROR) {
return NGX_ERROR;
}

if (n != size) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
ngx_read_file_n " returned "
"only %z bytes instead of %z",
n, size);
return NGX_ERROR;
}
//pos指向缓存已经解析的位置
b->pos = b->start + len;
//last指向缓存的结尾
b->last = b->pos + n;
//start指向缓存的开始
start = b->start;

if (dump) {
dump->last = ngx_cpymem(dump->last, b->pos, size);
}
}


对于worker_processes 1;配置,我们的目的是将work_processes 和1两个字段添加到入参数组中arr,

首先考虑的是对worker_processes前的空格要忽略掉;

然后是结尾,遇到;就表示一次配置的读取完成;

那么代码实现如下:

1 for(;;){
2     ch = *b->pos++;
3     if (last_space) {
4         if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) {
5             continue;
6         }
7         switch (ch) {
8              default:
9                 last_space = 0;
10         }
11     }
12     else
13     {
14         if (ch == ' ' || ch == '\t' || ch == CR || ch == LF
15                        || ch == ';')
16         {
17             last_space = 1;
18             found = 1;
19         }
20         if (found) {
21             word = ngx_array_push(cf->args);
22             if (word == NULL) {
23                 return NGX_ERROR;
24             }
25             word->data = ngx_pnalloc(cf->pool, b->pos - 1 - start + 1);
26             if (word->data == NULL) {
27                 return NGX_ERROR;
28             }
29             if (ch == ';') {
30                 return NGX_OK;
31             }
32             found = 0;
33         }
34 }


以上代码只考虑了这种简单情况;

这时我们加入双引号或者单引号,就需要在解析到对应的token时标示上当前token是带有引号的,并且存放入参数组是需要去掉引号。由于单引号和双引号等价,我们只考虑单引号情况。

多了单引号的介入,我们想到的方法是增加tag来标示解释过程中的引号匹配,读到左单引号就要加tag(s_quoted)token结束时应该有右单引号。

加入引号后,解析到左单引号时,标记s_quoted标示,标示字段必须以右单引号结束。

对于有单引号的字段后边的解析要加一个逻辑,配对的右单引号右边必须紧跟一个空格,或者分号结束,因此加上了needspace约束标志,标示解析完带有单引号的字段后必须要跟一个空格再继续解析,否则格式错误。

for(;;){
ch = *b->pos++;
if (need_space) {
if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) {
last_space = 1;
need_space = 0;
continue;
}

if (ch == ';') {
return NGX_OK;
}

if (ch == ')') {
last_space = 1;
need_space = 0;

} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unexpected \"%c\"", ch);
return NGX_ERROR;
}
}
if (last_space) {
if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) {
continue;
}
switch (ch) {
case '\'':
start++;
s_quoted = 1;
last_space = 0;
continue;
default:
last_space = 0;
}
}
else
{
if (s_quoted) {
if (ch == '\'') {
s_quoted = 0;
need_space = 1;
found = 1;
}
}
else if (ch == ' ' || ch == '\t' || ch == CR || ch == LF || ch == ';')
{
last_space = 1;
found = 1;
}

if (found) {
word = ngx_array_push(cf->args);
if (word == NULL) {
return NGX_ERROR;
}
word->data = ngx_pnalloc(cf->pool, b->pos - 1 - start + 1);
if (word->data == NULL) {
return NGX_ERROR;
}
if (ch == ';') {
return NGX_OK;
}
found = 0;
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: