1. 首先在函数ngx_conf_parse 根据文件名申请buff
2.根据文件大小读取文件内容
3. 逐个读取字符
解析文件中的字符步骤:
一)last_space 为1的情况,表示刚开始解析或者是前面已经解析到一个关键字(一般以空格,分号, 左大括号等为边界).
1. pos指针一个字符一个字符往右移动 ch = *pos++
2. 遇到空格,制表符(\t),回车,换行,直接跳过
3.更新局部变量start为pos指针-1
4.遇到分号,左大括号和右大括号直接return返回
5.遇到# 设置comment标记,continue
6.遇到\,’,”,设置对应标记,并将last_space 设置为0
7.其它情况设置last_space 为0
二)Last_sapce 不为0,解析关键字过程中
1. 遇到${,continue
2.遇到\, $等设置标记位,continue
3.遇到第二个双引号或者单引号,清空引号标记位,设置找到关键字标记found=1,并且设置后面跟着的字符必须有空格Need_space=1
4.遇到空格,\t,回车,换行,{,分号,设置找到关键字标记found=1,并设置last_space为1,重新开始寻找新的关键字.
5.找到关键字found=1,从存放关键字的数组cf->args找到一个空位置,将关键字存放到该字符串数组的空位中。如果是因为第4步骤的分号或者{设置的,这return OK或BLOKC_START。更新found为0
static ngx_int_t
ngx_conf_read_token(ngx_conf_t *cf)
{
u_char *start, ch, *src, *dst;
off_t file_size;
size_t len;
ssize_t n, size;
ngx_uint_t found, need_space, last_space, sharp_comment, variable;
ngx_uint_t quoted, s_quoted, d_quoted, start_line;
ngx_str_t *word;
ngx_buf_t *b;
found = 0;
need_space = 0;
last_space = 1;
sharp_comment = 0;
variable = 0;
quoted = 0;
s_quoted = 0;
d_quoted = 0;
cf->args->nelts = 0;/*设置保存参数的数组个数为0,即清空保存配置的数组*/
/*获取配置文件的buffer*/
b = cf->conf_file->buffer;
/*上一次读取结束的文件buf位置,第一次读取时为文件的起始位置*/
start = b->pos;
/*上一次读取的结束行数*/
start_line = cf->conf_file->line;
/*配置文件大小(字节)*/
file_size = ngx_file_size(&cf->conf_file->file.info);
ngx_conf_log_error(NGX_LOG_DEBUG,cf,0,"file_size=%lu,start_line=%uz",file_size,start_line);
for ( ;; ) {
/*第一次,b->pos = b->last,后续b->last指向读取buffer的结尾,
* 从第2次循环开始,如果为真,表示前一次读取的buffer已经全部解析完毕,需要进入判断一下是否文件已经读完,或者没有读完的话,继续读取
* 第2次以后if为真的情况是,文件的大小大于buffer的长度(4096),此时需要多次读取*/
if (b->pos >= b->last) {
/*如果偏移量大于文件大小,说明文件已读取完毕,返回NGX_CONF_FILE_DONE*/
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;
}
ngx_conf_log_error(NGX_LOG_DEBUG,cf,0,"start =%d,pos=%d",start,b->pos);
return NGX_CONF_FILE_DONE;
}
/*这个两个值在重新执行for循环的情况下应该相等,因为前面刚刚把b->pos赋值给了start*/
len = b->pos - start; /*len表示还有多少已扫描但是没有被解析*/
/*如果这个长度超过了最大文件buffer,NGX_CONF_BUFFER(4096),出错返回*/
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;
}
/**/
if (len) {
ngx_memmove(b->start, start, len);
}
size = (ssize_t) (file_size - cf->conf_file->file.offset);
/*即文件大小大于分配的buffer长度,size取为buffer长度*/
if (size > b->end - (b->start + len)) {
size = b->end - (b->start + len);
}
/*读取size大小的文件数据到buf中,起始地址为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;
}
/*修改当前buf的位置b->pos*/
b->pos = b->start + len;
/*指向数据的结尾*/
b->last = b->pos + n;
start = b->start;
}
/*一个字节一个字节地循环处理文件buff内容*/
ch = *b->pos++;
/*如果是换行符*/
if (ch == LF) {
cf->conf_file->line++;//处理的行数+1
if (sharp_comment) {//修改注释行标记位
sharp_comment = 0;
}
}
/*如果前面是#开头的注释,直接跳过#后面的所有字符,直到上面的遇到换行符后,
* 将该注释标记去除,表示注释结束,新的行开始0*/
if (sharp_comment) {
continue;
}
/*前面有反斜杠转义字符\,说明这个字符是一个转义字符,忽略\后面的字符*/
if (quoted) {
quoted = 0;
continue;
}
/*need_space默认为0,单引号或者双引号结束后,需要有以下分隔符:
* 空格,制表符,回车,换行,分号,左大括号,右小括号
* 遇到其它都是错误的,返回error*/
if (need_space) {
/*如果遇到空格,制表符,回车,换行,则将need_space清空,last_space置1*/
if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) {
last_space = 1;
need_space = 0;
continue;
}
/*如果遇到分号;则该行读取结束*/
if (ch == ';') {
return NGX_OK;
}
/*遇到'{'表示新的block的开始*/
if (ch == '{') {
return NGX_CONF_BLOCK_START;
}
if (ch == ')') {
last_space = 1;
need_space = 0;
} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unexpected \"%c\"", ch);
return NGX_ERROR;
}
}
/*last_space初始化为1,last_space表示上一个字符为字符串分隔符,需要重新计算start,以表示新的token的起始地址*/
if (last_space) {
/*遇到空格,制表符,回车,换行等字符,则无需处理*/
if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) {
continue;
}
start = b->pos - 1;/*更新start值,作为新的token(关键字)的起始地址*/
start_line = cf->conf_file->line;
switch (ch) {
/*遇到;或者{,直接返回*/
case ';':
case '{':
if (cf->args->nelts == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unexpected \"%c\"", ch);
return NGX_ERROR;
}
if (ch == '{') {
return NGX_CONF_BLOCK_START;
}
return NGX_OK;
case '}': //遇到}也返回
if (cf->args->nelts != 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unexpected \"}\"");
return NGX_ERROR;
}
return NGX_CONF_BLOCK_DONE;
case '#':/*遇到#,设置sharp_comment标记*/
sharp_comment = 1;
continue;
case '\\':/*遇到\,设置quoted=1,last_space=0*/
quoted = 1;
last_space = 0;
continue;
case '"'://遇到第一个“,设置d_quoted为1
start++;
d_quoted = 1;
last_space = 0;
continue;
case '\''://遇到第一个单引号',设置s_quoted为1
start++;
s_quoted = 1;
last_space = 0;
continue;
default:
last_space = 0;
}
} else {
if (ch == '{' && variable) {
continue;
}
variable = 0;
if (ch == '\\') {
quoted = 1;
continue;
}
/*遇到$符,设置variable为1*/
if (ch == '$') {
variable = 1;
continue;
}
if (d_quoted) {
if (ch == '"') {//遇到第二个双引号”,设置d_quoted=0,need_space=1,found=1
d_quoted = 0;
need_space = 1;
found = 1;
}
} else if (s_quoted) {//遇到第二个单引号’,设置s_quoted=0,need_space=1,found=1
if (ch == '\'') {
s_quoted = 0;
need_space = 1;
found = 1;
}
} else if (ch == ' ' || ch == '\t' || ch == CR || ch == LF
|| ch == ';' || ch == '{')//遇到空格,制表符,回车,换行符,分号,{,设置last_space=1,found=1
{
last_space = 1;
found = 1;
}
//正常情况下,遇到空格或分号结束时,设置了last_space=1,found=1
if (found) {
/*args是初始化为10个ngx_str_t大小的动态数组,在ngx_init_cycle中初始化
* 存放配置文件中的一行中的关键字,如:
* worker_processes 1;
* args的第一个数组元素就是 worker_process
* 第二个数组元素就是1*/
word = ngx_array_push(cf->args);//找到数组可用的位置
if (word == NULL) {
return NGX_ERROR;
}
/*文件数据的起始位置为start,当前的位置为b->pos*/
word->data = ngx_pnalloc(cf->pool, b->pos - start + 1);
if (word->data == NULL) {
return NGX_ERROR;
}
/*将start开始的数据拷贝到word->data中,并记录拷贝的大小len*/
for (dst = word->data, src = start, len = 0;
src < b->pos - 1;
len++)
{
if (*src == '\\') {
switch (src[1]) {
case '"':
case '\'':
case '\\':
src++;
break;
case 't':
*dst++ = '\t';
src += 2;
continue;
case 'r':
*dst++ = '\r';
src += 2;
continue;
case 'n':
*dst++ = '\n';
src += 2;
continue;
}
}
*dst++ = *src++;
}
*dst = '\0';
word->len = len;
/*正常情况下,遇到分号,返回一行*/
if (ch == ';') {
return NGX_OK;
}
/*如果是一个block,如event,则返回NGX_CONF_BLOCK_START*/
if (ch == '{') {
return NGX_CONF_BLOCK_START;
}
/*等于其它的符号,如空格,则继续循环*/
found = 0;
}
}
}
}