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