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

我们知道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 if (filename) {
 2 
 3     /* open configuration file */
 4 
 5     fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
 6     if (fd == NGX_INVALID_FILE) {
 7         ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
 8                            ngx_open_file_n " \"%s\" failed",
 9                            filename->data);
10         return NGX_CONF_ERROR;
11     }
12 
13     prev = cf->conf_file;
14 
15     cf->conf_file = &conf_file;
16     //ngx_fd_info 将fd文件的状态拷贝到sb中
17     //define ngx_fd_info(fd, sb)      fstat(fd, sb)
18     //typedef struct stat              ngx_file_info_t;
19     if (ngx_fd_info(fd, &cf->conf_file->file.info) == NGX_FILE_ERROR) {
20         ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno,
21                       ngx_fd_info_n " \"%s\" failed", filename->data);
22     }
23 
24     cf->conf_file->buffer = &buf;
25 
26     buf.start = ngx_alloc(NGX_CONF_BUFFER, cf->log);
27     if (buf.start == NULL) {
28         goto failed;
29     }
30 
31     buf.pos = buf.start;
32     buf.last = buf.start;
33     buf.end = buf.last + NGX_CONF_BUFFER;
34     buf.temporary = 1;
35 
36     cf->conf_file->file.fd = fd;
37     cf->conf_file->file.name.len = filename->len;
38     cf->conf_file->file.name.data = filename->data;
39     cf->conf_file->file.offset = 0;
40     cf->conf_file->file.log = cf->log;
41     cf->conf_file->line = 1;
42 
43     type = parse_file;
44 }
View Code

文件打开后,并且缓存准备就绪,就可以开始读文件了。

1  for ( ;; ) {
2         rc = ngx_conf_read_token(cf);
3 }

进入循环进行配置文件的读取。我们进入ngx_conf_read_token看下。

函数的开始同样是一些基础数据的声明,这里先放这里,可以回头看下。

 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;
        }
    }
}

 

posted @ 2017-02-28 22:43  丶丨zuoluo  阅读(762)  评论(0编辑  收藏  举报