目录

ngx_conf_parse

目录

ngx_conf_parse

ngx_conf_parse

定义src/core/ngx_conf_file.c

char *
ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename)
{
    char             *rv;
    ngx_fd_t          fd;
    ngx_int_t         rc;
    ngx_buf_t         buf;
    ngx_conf_file_t  *prev, conf_file;
    enum {
        parse_file = 0,
        parse_block,
        parse_param
    } type;

#if (NGX_SUPPRESS_WARN)
    fd = NGX_INVALID_FILE;
    prev = NULL;
#endif

    if (filename) {

        /* open configuration file */

        fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);

        if (fd == NGX_INVALID_FILE) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
                               ngx_open_file_n " \"%s\" failed",
                               filename->data);
            return NGX_CONF_ERROR;
        }

        prev = cf->conf_file;

        cf->conf_file = &conf_file;

        if (ngx_fd_info(fd, &cf->conf_file->file.info) == NGX_FILE_ERROR) {
            ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno,
                          ngx_fd_info_n " \"%s\" failed", filename->data);
        }

        cf->conf_file->buffer = &buf;

        buf.start = ngx_alloc(NGX_CONF_BUFFER, cf->log);
        if (buf.start == NULL) {
            goto failed;
        }

        buf.pos = buf.start;
        buf.last = buf.start;
        buf.end = buf.last + NGX_CONF_BUFFER;
        buf.temporary = 1;

        cf->conf_file->file.fd = fd;
        cf->conf_file->file.name.len = filename->len;
        cf->conf_file->file.name.data = filename->data;
        cf->conf_file->file.offset = 0;
        cf->conf_file->file.log = cf->log;
        cf->conf_file->line = 1;

        type = parse_file;

        if (ngx_dump_config
#if (NGX_DEBUG)
            || 1
#endif
           )
        {
            if (ngx_conf_add_dump(cf, filename) != NGX_OK) {
                goto failed;
            }

        } else {
            cf->conf_file->dump = NULL;
        }

    } else if (cf->conf_file->file.fd != NGX_INVALID_FILE) {

        type = parse_block;

    } else {
        type = parse_param;
    }


    for ( ;; ) {
        rc = ngx_conf_read_token(cf);

        /*
         * ngx_conf_read_token() may return
         *
         *    NGX_ERROR             there is error
         *    NGX_OK                the token terminated by ";" was found
         *    NGX_CONF_BLOCK_START  the token terminated by "{" was found
         *    NGX_CONF_BLOCK_DONE   the "}" was found
         *    NGX_CONF_FILE_DONE    the configuration file is done
         */

        if (rc == NGX_ERROR) {
            goto done;
        }

        if (rc == NGX_CONF_BLOCK_DONE) {

            if (type != parse_block) {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"}\"");
                goto failed;
            }

            goto done;
        }

        if (rc == NGX_CONF_FILE_DONE) {

            if (type == parse_block) {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                   "unexpected end of file, expecting \"}\"");
                goto failed;
            }

            goto done;
        }

        if (rc == NGX_CONF_BLOCK_START) {

            if (type == parse_param) {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                   "block directives are not supported "
                                   "in -g option");
                goto failed;
            }
        }

        /* rc == NGX_OK || rc == NGX_CONF_BLOCK_START */

        if (cf->handler) {

            /*
             * the custom handler, i.e., that is used in the http's
             * "types { ... }" directive
             */

            if (rc == NGX_CONF_BLOCK_START) {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"{\"");
                goto failed;
            }

            rv = (*cf->handler)(cf, NULL, cf->handler_conf);
            if (rv == NGX_CONF_OK) {
                continue;
            }

            if (rv == NGX_CONF_ERROR) {
                goto failed;
            }

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", rv);

            goto failed;
        }


        rc = ngx_conf_handler(cf, rc);

        if (rc == NGX_ERROR) {
            goto failed;
        }
    }

failed:

    rc = NGX_ERROR;

done:

    if (filename) {
        if (cf->conf_file->buffer->start) {
            ngx_free(cf->conf_file->buffer->start);
        }

        if (ngx_close_file(fd) == NGX_FILE_ERROR) {
            ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
                          ngx_close_file_n " %s failed",
                          filename->data);
            rc = NGX_ERROR;
        }

        cf->conf_file = prev;
    }

    if (rc == NGX_ERROR) {
        return NGX_CONF_ERROR;
    }

    return NGX_CONF_OK;
}

ngx_conf_parse 是 Nginx 中解析配置文件的核心函数,其作用和意义可以通俗理解为:

这个函数就像 Nginx 的“翻译官”,把用户写的配置文件“翻译”成程序能理解的指令,并确保语法正确、资源合理使用,是 Nginx 启动和配置生效的关键环节。


char *ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename);

函数签名

1. 返回值类型 char *

作用:返回配置解析的结果状态。

可能值: NGX_CONF_OK:解析成功(通常定义为 NULL 或特定宏)。 NGX_CONF_ERROR:解析失败(通常指向一个静态错误字符串或特定标记)。 特殊设计:Nginx 中许多函数通过返回 char* 表示错误信息。若返回 NULL(即 NGX_CONF_OK),表示成功;若返回非空字符串(如 “invalid option”),表示错误原因。

2. 参数 ngx_conf_t *cf

类型:ngx_conf_t 是配置解析的上下文结构体,包含解析过程所需的所有状态信息。

作用:维护解析过程中的全局状态,协调不同模块的配置指令。

3. 参数 ngx_str_t *filename 类型:ngx_str_t 是 Nginx 的字符串类型

作用:

若 filename 非空:表示需要解析的配置文件路径(如 nginx.conf)。 若 filename 为空(NULL):表示从其他来源解析配置(如命令行参数 -g 指定的配置片段) 特殊逻辑:

当解析文件时,会打开文件并读取内容到缓冲区。 当解析命令行参数时,直接处理内存中的字符串(无需打开文件)。


    enum {
        parse_file = 0,
        parse_block,
        parse_param
    } type;

作用:定义解析模式枚举,控制解析行为。

枚举值含义: parse_file :解析普通配置文件(默认模式)。 parse_block :解析代码块(如 http { … })。 parse_param :解析命令行参数(如 nginx -g “daemon off;")。

背景: 不同模式下语法校验规则不同。

意图: 通过状态机模式统一处理不同输入源,避免代码冗余。


    if (filename) {
  • 作用 :判断是否需要解析一个具体的配置文件(而非命令行参数或代码块)

此次运行情况:

filename=/home/wsd/桌面/nginx/conf/nginx.conf


fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);

if (fd == NGX_INVALID_FILE) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
                               ngx_open_file_n " \"%s\" failed",
                               filename->data);
            return NGX_CONF_ERROR;
        }

打开配置文件,返回文件描述符 fd

filename->data 指定要打开的文件路径

NGX_FILE_RDONLY 指定文件以 只读模式 打开

NGX_FILE_OPEN

指定文件打开模式为“仅打开现有文件”。

若文件不存在, NGX_FILE_OPEN 会阻止自动创建新文件(与 O_CREAT 标志相反)

0

指定新创建文件的权限

由于第三个参数是 NGX_FILE_OPEN (不创建新文件),此参数会被忽略。 传递 0 ,明确表示“无需设置权限”。

  • 错误处理 : 若打开失败(如文件不存在),记录日志并返回 NGX_CONF_ERROR


        prev = cf->conf_file;

保存当前配置文件的上下文

当解析嵌套的 include 文件时,需暂存父级上下文,解析完成后恢复

此时的运行情况:

prev=null

第一次执行到这里时 , cf->conf_file 还没有设置


        cf->conf_file = &conf_file;

将当前配置文件上下文切换为新打开的文件

        if (ngx_fd_info(fd, &cf->conf_file->file.info) == NGX_FILE_ERROR) {
            ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno,
                          ngx_fd_info_n " \"%s\" failed", filename->data);
        }

获取文件元信息(如大小、修改时间),存储到 cf->conf_file->file.info

        cf->conf_file->buffer = &buf;

将缓冲区 buf 关联到当前配置文件上下文

        buf.start = ngx_alloc(NGX_CONF_BUFFER, cf->log);
        if (buf.start == NULL) {
            goto failed;
        }

分配 NGX_CONF_BUFFER 大小的内存(4KB),用于存储文件内容

        buf.pos = buf.start;
初始化缓冲区的当前读取位置指针

pos 指向缓冲区的起始位置,表示尚未读取任何数据

        buf.last = buf.start;

last 指向缓冲区的起始位置,表示缓冲区当前为空

        buf.end = buf.last + NGX_CONF_BUFFER;

标记缓冲区的结束位置

        buf.temporary = 1;

标记缓冲区为临时内存

        cf->conf_file->file.fd = fd;

将文件描述符 fd 关联到配置文件上下文

后续读取文件时直接使用该 fd

        cf->conf_file->file.name.len = filename->len;
        cf->conf_file->file.name.data = filename->data;
        cf->conf_file->file.offset = 0;
        cf->conf_file->file.log = cf->log;
        cf->conf_file->line = 1;
        type = parse_file;

设置解析模式为 parse_file (普通文件模式)

       if (ngx_dump_config

ngx_dump_config 是全局变量,控制是否保存配置内容

此时的情况:

ngx_dump_config=0

所以进入 else

else {
            cf->conf_file->dump = NULL;
        }

此时的情况:

cf->conf_file->dump = NULL

接下来是:

   for ( ;; ) {
  

创建无限循环,持续处理配置指令直到遇到终止条件 配置解析需要顺序处理所有指令,直到文件结束或语法错误

      rc = ngx_conf_read_token(cf);

读取并解析下一个配置token(指令/符号)

该函数负责词法分析,识别分号、大括号等语法结构


进入 ngx_conf_read_token


接下来:

       /* rc == NGX_OK || rc == NGX_CONF_BLOCK_START */

        if (cf->handler) {

此时 cf->handler 是 NULL

        rc = ngx_conf_handler(cf, rc);

调用 ngx_conf_handler 函数处理当前解析到的配置指令