配置文件解析

2015-01-01 12:18:16   最后更新: 2015-09-20 20:34:07   访问数量:720




在一个工程中,配置文件的解析是一个非常重要的工作,为工程的实践提供了巨大的便捷性与灵活性

ngx_memzero(&conf, sizeof(ngx_conf_t)); /* STUB: init array ? */ // 保存配置文件中所有指令 conf.args = ngx_array_create(pool, 10, sizeof(ngx_str_t)); if (conf.args == NULL) { ngx_destroy_pool(pool); return NULL; } conf.temp_pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log); if (conf.temp_pool == NULL) { ngx_destroy_pool(pool); return NULL; } conf.ctx = cycle->conf_ctx; conf.cycle = cycle; conf.pool = pool; conf.log = log; conf.module_type = NGX_CORE_MODULE; conf.cmd_type = NGX_MAIN_CONF; #if 0 log->log_level = NGX_LOG_DEBUG_ALL; #endif // 解析命令行传递的配置 if (ngx_conf_param(&conf) != NGX_CONF_OK) { environ = senv; ngx_destroy_cycle_pools(&conf); return NULL; } // 解析配置文件 if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) { environ = senv; ngx_destroy_cycle_pools(&conf); return NULL; } if (ngx_test_config && !ngx_quiet_mode) { ngx_log_stderr(0, "the configuration file %s syntax is ok", cycle->conf_file.data); }

 

 

// struct ngx_conf_file_t // 配置文件信息 {{{ typedef struct { ngx_file_t file; // 文件描述 ngx_buf_t *buffer; // 文件缓存 ngx_uint_t line; // 当前读取位置 } ngx_conf_file_t; // }}} typedef char *(*ngx_conf_handler_pt)(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); // struct ngx_conf_s // 保存配置文件中指令 {{{ struct ngx_conf_s { char *name; // 指令名称 ngx_array_t *args; // 指令参数 ngx_cycle_t *cycle; ngx_pool_t *pool; // 内存池 ngx_pool_t *temp_pool; // 用于解析配置文件的临时内存池 ngx_conf_file_t *conf_file; // 配置文件 ngx_log_t *log; // 日志 void *ctx; // 描述指令的上下文 ngx_uint_t module_type; // 指令模块类型(core、http、event或mail) ngx_uint_t cmd_type; // 指令类型 ngx_conf_handler_pt handler; // 处理指令的回调函数 char *handler_conf; // 回调函数需要的相关配置 }; // }}}

 

ngx_memzero(&conf, sizeof(ngx_conf_t)); /* STUB: init array ? */ // 保存配置文件中所有指令 conf.args = ngx_array_create(pool, 10, sizeof(ngx_str_t)); if (conf.args == NULL) { ngx_destroy_pool(pool); return NULL; } conf.temp_pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log); if (conf.temp_pool == NULL) { ngx_destroy_pool(pool); return NULL; } conf.ctx = cycle->conf_ctx; conf.cycle = cycle; conf.pool = pool; conf.log = log; conf.module_type = NGX_CORE_MODULE; conf.cmd_type = NGX_MAIN_CONF;

 

这段代码初始化了局部变量 conf,他是一个配置文件指令结构,保存了配置文件中的所有指令

初始化后,conf 的值为:

{ name = 0x0, args = 0x81000f4, <ngx_array_t *> { elts = 0x8100108, nelts = 0, size = 8, nalloc = 10, pool = 0x80ff700 // cycle->pool } cycle = 0x80ff728, pool = 0x80ff700, // cycle->pool temp_pool = 0x8103730, conf_file = 0x0, log = 0x80e3020 <ngx_log>, // cycle->log { log_level = 6, file = 0x80e3000 <ngx_log_file>, connection = 0, handler = 0x0, data = 0x0, writer = 0x0, wdata = 0x0, action = 0x0, next = 0x0 } ctx = 0x80fff38, module_type = 1163022147, // NGX_CORE_MODULE cmd_type = 16777216, // NGX_MAIN_CONF handler = 0x0, handler_conf = 0x0 }

 

 

// 解析命令行传递的配置 if (ngx_conf_param(&conf) != NGX_CONF_OK) { environ = senv; ngx_destroy_cycle_pools(&conf); return NULL; }

 

 

// char * ngx_conf_param(ngx_conf_t *cf) // 解析命令行传递的配置 {{{ char * ngx_conf_param(ngx_conf_t *cf) { char *rv; ngx_str_t *param; ngx_buf_t b; ngx_conf_file_t conf_file; // 获取系统配置参数 param = &cf->cycle->conf_param; if (param->len == 0) { return NGX_CONF_OK; } ngx_memzero(&conf_file, sizeof(ngx_conf_file_t)); ngx_memzero(&b, sizeof(ngx_buf_t)); b.start = param->data; b.pos = param->data; b.last = param->data + param->len; b.end = b.last; b.temporary = 1; conf_file.file.fd = NGX_INVALID_FILE; conf_file.file.name.data = NULL; conf_file.line = 0; cf->conf_file = &conf_file; cf->conf_file->buffer = &b; rv = ngx_conf_parse(cf, NULL); cf->conf_file = NULL; return rv; } // }}}

 

 

// char * ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename) // 解析配置文件 {{{ 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; } 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, 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; } // }}}

 

首先打开了 cycle->conf_file 中保存的配置文件完整路径中保存的配置文件(/etc/nginx/nginx.conf)

然后调用系统调用 fstat 获取了配置文件状态

#define ngx_fd_info(fd, sb) fstat(fd, sb)

 

 

文件结构 ngx_file_s 中的 info 字段是一个 ngx_file_info_t 类型

typedef struct stat ngx_file_info_t;

 

用来保存 fstat 返回的值-结果参数 sb

以下是所有根据 struct stat 结构获取文件属性的宏

#define ngx_file_access(sb) ((sb)->st_mode & 0777) #define ngx_file_size(sb) (sb)->st_size #define ngx_file_fs_size(sb) ngx_max((sb)->st_size, (sb)->st_blocks * 512) #define ngx_file_mtime(sb) (sb)->st_mtime #define ngx_file_uniq(sb) (sb)->st_ino

 

 

然后创建了文件缓存及相关初始化工作

全部初始化后,conf_file 取值为:

{ name = 0x0, args = 0x81000f4, <ngx_array_t *> { elts = 0x8100108, nelts = 0, size = 8, nalloc = 10, pool = 0x80ff700 } cycle = 0x80ff728, pool = 0x80ff700, // cycle->pool temp_pool = 0x8103730, conf_file = 0xbffff244, <ngx_conf_file_t *> { file = { fd = 4, name = { len = 21, data = 0x80ff845 "/etc/nginx/nginx.conf" }, info = { st_dev = 51713, __pad1 = 0, __st_ino = 278957, st_mode = 33188, st_nlink = 1, st_uid = 0, st_gid = 0, st_rdev = 0, __pad2 = 0, st_size = 2658, st_blksize = 4096, st_blocks = 8, st_atim = { tv_sec = 1420009355, tv_nsec = 826737343 }, st_mtim = { tv_sec = 1419560147, tv_nsec = 877815145 }, st_ctim = { tv_sec = 1419560147, tv_nsec = 877815145 }, st_ino = 278957 }, offset = 0, sys_offset = 581025031364015872, log = 0x80e3020 <ngx_log>, valid_info = 0, directio = 0 }, buffer = 0xbffff210, <ngx_buf_t *> { pos = 0x8107748 "", last = 0x8107748 "", file_pos = 70471823392792, file_last = 4121110559454356804, start = 0x8107748 "", end = 0x8108748 "", tag = 0x10, file = 0x4000, shadow = 0xb7d28420 <main_arena>, temporary = 1, memory = 0, mmap = 0, recycled = 0, in_file = 1, flush = 1, sync = 0, last_buf = 1, last_in_chain = 1, last_shadow = 1, temp_file = 0, num = 16384 } line = 1 } log = 0x80e3020, <ngx_log_t *> { log_level = 6, file = 0x80e3000 <ngx_log_file>, connection = 0, handler = 0x0, data = 0x0, writer = 0x0, wdata = 0x0, action = 0x0, next = 0x0 } ctx = 0x80fff38, module_type = 1163022147, cmd_type = 16777216, handler = 0x0, handler_conf = 0x0 }

 

 

typedef void * ngx_buf_tag_t; // struct ngx_buf_s // 内存缓冲区 {{{ struct ngx_buf_s { u_char *pos; // 当前读取缓存位置 u_char *last; // 缓存内容终止位置 off_t file_pos; // 缓存在文件中的偏移量 off_t file_last; // 缓存终止位置在文件中的偏移量 // 内存块指针,内存块值得是缓存中的某一具体数据块,具体意义随应用相关 u_char *start; /* start of buffer */ // 内存块起始地址 u_char *end; /* end of buffer */ // 内存块终止地址 ngx_buf_tag_t tag; // 标签 ngx_file_t *file; // 缓存所对应的文件 ngx_buf_t *shadow; // 复制来源 /* the buf's content could be changed */ unsigned temporary:1; // 临时缓存 /* * the buf's content is in a memory cache or in a read only memory * and must not be changed */ unsigned memory:1; // 内存缓存 /* the buf's content is mmap()ed and must not be changed */ unsigned mmap:1; // 内存映射缓存 unsigned recycled:1; unsigned in_file:1; // 文件缓存 unsigned flush:1; unsigned sync:1; unsigned last_buf:1; unsigned last_in_chain:1; unsigned last_shadow:1; unsigned temp_file:1; /* STUB */ int num; }; // }}}

 

这个类型保存了文件、内存的缓存信息,用来管理相应的缓存

 

在 配置文件解析过程中,创建了相应的文件缓存:

// 创建文件缓存 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;

 

上述过程初始化后 buf 取值为:

{ pos = 0x8107748 "", last = 0x8107748 "", file_pos = 70471823392792, // 随机值,未初始化 file_last = 4121110559454356804, // 随机值,未初始化 start = 0x8107748 "", end = 0x8108748 "", tag = 0x10, // 随机值,未初始化 file = 0x4000, // 随机值,未初始化 shadow = 0xb7d28420 <main_arena>, // 随机值,未初始化 temporary = 1, memory = 0, mmap = 0, recycled = 0, in_file = 1, flush = 1, sync = 0, last_buf = 1, last_in_chain = 1, last_shadow = 1, temp_file = 0, num = 16384 }

 

由于缓存是临时性的存储,所以没有开辟到内存池中

 

接下来就要对打开的配置文件进行读取了

nginx 使用 ngx_conf_read_token 这个函数依次取出并读取每一条指令

// static ngx_int_t ngx_conf_read_token(ngx_conf_t *cf) // 取出并读取配置指令 {{{ 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; b = cf->conf_file->buffer; start = b->pos; start_line = cf->conf_file->line; file_size = ngx_file_size(&cf->conf_file->file.info); for ( ;; ) { // 判断是否是首次使用 b->last 永远指向缓存起始位置 if (b->pos >= b->last) { 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; } if (len) { // 缓存文件内容 ngx_memmove(b->start, start, len); } 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; } b->pos = b->start + len; // 将last指针移向有效缓存区末尾 b->last = b->pos + n; start = b->start; } ch = *b->pos++; if (ch == LF) { cf->conf_file->line++; if (sharp_comment) { sharp_comment = 0; } } // 是注释 if (sharp_comment) { continue; } if (quoted) { quoted = 0; continue; } // 需要结束标志 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 == '{') { 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; } } if (last_space) { if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) { continue; } start = b->pos - 1; 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 = 1; continue; case '\\': quoted = 1; last_space = 0; continue; case '"': start++; d_quoted = 1; last_space = 0; continue; case '\'': start++; s_quoted = 1; last_space = 0; continue; default: last_space = 0; } } else { if (ch == '{' && variable) { continue; } variable = 0; if (ch == '\\') { quoted = 1; continue; } if (ch == '$') { variable = 1; continue; } if (d_quoted) { if (ch == '"') { d_quoted = 0; need_space = 1; found = 1; } } else if (s_quoted) { 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; } if (found) { word = ngx_array_push(cf->args); if (word == NULL) { return NGX_ERROR; } word->data = ngx_pnalloc(cf->pool, b->pos - start + 1); if (word->data == NULL) { return NGX_ERROR; } // 为 args 新元素赋值 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; } if (ch == '{') { return NGX_CONF_BLOCK_START; } found = 0; } } } } // }}}

 

首先,通过 struct stat 结构域获取了文件大小信息

然后判断是否已经到达结尾,如果没有到达结尾,则使用系统调用 memmove 将文件内容起始至今的内容拷贝到缓存中

接着,调用系统调用 pread 原子地打开文件,将文件读取到缓存中

缓存结构的 last 域永远指向有效缓存区末尾,而 pos 指向未读取内容起始,start 指向有价值的内存的起始位置,也就是说,当读取到连续的字符串直到遇到空白字符,就可以根据作为标识符的局部变量,判断出从 start 到 pos 的内存是否是变量或变量的值了,即可加入到 cf->args 数组中

 

然后对接下来循环的依次对一个字符进行解析

 

这个函数每次返回一个配置项的配置,保存在值-结果参数 cf 中作为返回,参数以键值对的方式保存在 cf->args 数组中

 

// static ngx_int_t ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last) // 配置项解析 {{{ static ngx_int_t ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last) { char *rv; void *conf, **confp; ngx_uint_t i, found; ngx_str_t *name; ngx_command_t *cmd; name = cf->args->elts; found = 0; for (i = 0; ngx_modules[i]; i++) { cmd = ngx_modules[i]->commands; if (cmd == NULL) { continue; } for ( /* void */ ; cmd->name.len; cmd++) { if (name->len != cmd->name.len) { continue; } if (ngx_strcmp(name->data, cmd->name.data) != 0) { continue; } found = 1; if (ngx_modules[i]->type != NGX_CONF_MODULE && ngx_modules[i]->type != cf->module_type) { continue; } /* is the directive's location right ? */ if (!(cmd->type & cf->cmd_type)) { continue; } if (!(cmd->type & NGX_CONF_BLOCK) && last != NGX_OK) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "directive \"%s\" is not terminated by \";\"", name->data); return NGX_ERROR; } if ((cmd->type & NGX_CONF_BLOCK) && last != NGX_CONF_BLOCK_START) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "directive \"%s\" has no opening \"{\"", name->data); return NGX_ERROR; } /* is the directive's argument count right ? */ if (!(cmd->type & NGX_CONF_ANY)) { if (cmd->type & NGX_CONF_FLAG) { if (cf->args->nelts != 2) { goto invalid; } } else if (cmd->type & NGX_CONF_1MORE) { if (cf->args->nelts < 2) { goto invalid; } } else if (cmd->type & NGX_CONF_2MORE) { if (cf->args->nelts < 3) { goto invalid; } } else if (cf->args->nelts > NGX_CONF_MAX_ARGS) { goto invalid; } else if (!(cmd->type & argument_number[cf->args->nelts - 1])) { goto invalid; } } /* set up the directive's configuration context */ conf = NULL; if (cmd->type & NGX_DIRECT_CONF) { conf = ((void **) cf->ctx)[ngx_modules[i]->index]; } else if (cmd->type & NGX_MAIN_CONF) { conf = &(((void **) cf->ctx)[ngx_modules[i]->index]); } else if (cf->ctx) { confp = *(void **) ((char *) cf->ctx + cmd->conf); if (confp) { conf = confp[ngx_modules[i]->ctx_index]; } } // 调用命令创建回调函数 rv = cmd->set(cf, cmd, conf); if (rv == NGX_CONF_OK) { return NGX_OK; } if (rv == NGX_CONF_ERROR) { return NGX_ERROR; } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"%s\" directive %s", name->data, rv); return NGX_ERROR; } } if (found) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"%s\" directive is not allowed here", name->data); return NGX_ERROR; } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unknown directive \"%s\"", name->data); return NGX_ERROR; invalid: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid number of arguments in \"%s\" directive", name->data); return NGX_ERROR; } // }}}

 

 

这个函数中,通过匹配模块配置结构体中的 commands 域中所有的命令,调用配置中的 set 回调函数实现对各个配置块的解析

 






技术帖      配置文件      龙潭书斋      服务器      内存      nginx      conf      ngx_conf_file_t      ngx_conf_s      ngx_conf_param      ngx_conf_parse      ngx_buf_s      ngx_buf_t      缓存     


京ICP备15018585号