当前位置: 首页 > article >正文

Ubuntu 下 nginx-1.24.0 源码分析 - ngx_conf_read_token - 详解(1)

详解(1)


cf->args->nelts = 0;
  • 作用:清空参数数组,准备解析新参数。
    cf->args存储当前指令的参数列表
    cf->args 是一个指向 ngx_array_t 的指针,本质是一个动态数组。
    数组中的每个元素是 ngx_str_t 类型,表示一个参数字符串
b = cf->conf_file->buffer;
dump = cf->conf_file->dump;
  • 作用:获取配置文件缓冲区和dump缓冲区指针。

start = b->pos;
start_line = cf->conf_file->line;

start 记录当前 token(即配置项参数)的起始位置
start = b->pos; 表示将当前解析位置标记为新 token 的起始位置

start_line 记录当前 token 的起始行号,用于错误提示和调试


file_size = ngx_file_size(&cf->conf_file->file.info);

获取配置文件总大小,判断是否读取完毕。


for ( ;; ) {

for (;;) 循环的主要作用是实现逐字符解析配置文件内容,直到完成整个文件的解析或遇到错误


if (b->pos >= b->last) {

检测当前缓冲区是否已处理完所有数据


if (cf->conf_file->file.offset >= file_size) {

判断是否已读取完整个配置文件
off_t(文件偏移量)
表示当前已读取的文件字节数(即文件指针的位置)
file_size 配置文件的总大小

如果成立,表示已读取完整个配置文件(所有字节已处理)。
否则,表示文件尚未读取完毕,需要继续读取


if (cf->args->nelts > 0 || !last_space) {

检测文件结束时是否存在未完成的语法结构
如果 nelts > 0,表示当前指令已解析了部分参数,但尚未遇到结束符

last_space 是标志位,表示最后一个处理的字符是否是空白符(空格、换行等)
如果 !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;
                    }

当配置文件的读取位置已经到达文件末尾时,检查是否还有未完成的参数或语法错误。如果有,则根据文件描述符是否有效,输出不同的错误信息。

区分配置来源(内存或文件)并输出精准的错误提
如果 fd == NGX_INVALID_FILE:表示当前解析的配置内容来自内存(如命令行参数或直接传递的字符串)。
否则:表示配置内容来自磁盘文件。


return NGX_CONF_FILE_DONE;

配置文件已全部读取且无语法错误 时 返回该值


len = b->pos - start;

b->pos >= b->last(缓冲区数据已处理完毕)时,需要从文件中读取新数据。此时:

start :指向当前 token 的起始位置(可能跨缓冲区)。
b->pos :已到达缓冲区末尾(b->last)。
通过 len = b->pos - start;:

计算剩余未处理数据的长度 :即从 start 到缓冲区末尾(b->last)的字节数

在缓冲区耗尽时,确定需要迁移的数据量
确保 Token 完整性 :跨缓冲区的 token 能被正确拼接和解析


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_CONF_BUFFER) {

检测参数长度是否超过缓冲区容量

  • NGX_CONF_BUFFER
    为缓冲区的固定大小
    表示单次读取配置文件的块大小。

  • len == NGX_CONF_BUFFER 时,表示:

    • 当前 token 或未处理数据的长度已达到缓冲区最大容量(如 4KB)。
    • 但尚未遇到参数结束符(如引号闭合、分号 ; 或块开始符 {)。

cf->conf_file->line = start_line;

重置行号
将错误日志的行号重置为当前 token 的起始行号(start_line)。
确保错误信息指向参数的开始位置,而非当前解析位置。

if (d_quoted) {
    ch = '"';
} else if (s_quoted) {
    ch = '\'';
} else {
    // 非引号内的超长参数
    ngx_conf_log_error(...);
    return NGX_ERROR;
}

判断未闭合的引号类型


d_quoted(双引号标志)

  • 含义d_quoted1 时表示解析器当前处于双引号包裹的上下文中。
  • 触发条件:当解析器遇到字符 " 时,设置 d_quoted = 1
  • 退出条件:当再次遇到 " 时,设置 d_quoted = 0
  • 行为影响
    • 在双引号内,空格和特殊字符(如 ;{)会被视为普通字符,而非分隔符或语法符号。
    • 支持转义字符(如 \" 表示双引号本身,\t 表示制表符)。
    • 示例:
      proxy_set_header X-Name "Hello \"World\"";  # 双引号内的内容被完整解析
      

s_quoted(单引号标志)

  • 含义s_quoted1 时表示解析器当前处于单引号包裹的上下文中。
  • 触发条件:当解析器遇到字符 ' 时,设置 s_quoted = 1
  • 退出条件:当再次遇到 ' 时,设置 s_quoted = 0
  • 行为影响
    • 在单引号内,所有字符(包括空格和特殊字符)均被视为普通字符
    • 不支持转义字符(单引号内的 \ 会被视为普通字符)。

  • 当参数长度超过缓冲区(NGX_CONF_BUFFER)时,根据 d_quoteds_quoted 的状态,输出不同的错误提示:

输出错误信息
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                   "too long parameter, probably missing terminating \"%c\" character",
                   ch);
  • 内容
    • 提示参数过长,并指出可能缺少闭合的引号字符(如 "')。
    • 示例输出:
      too long parameter, probably missing terminating '"' character
      

else {
                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                       "too long parameter \"%*s...\" started",
                                       10, start);
                    return NGX_ERROR;
                }

非引号内的超长参数
错误日志记录

  • 参数解释
    • NGX_LOG_EMERG:日志级别为紧急(最高级别,表示严重错误)。
    • "%*s...":格式化字符串,%*s 表示动态宽度的字符串。
    • 10:动态宽度值,表示最多显示前 10 个字符。
    • start:指向参数起始位置的指针。
  • 输出示例
    too long parameter "aaaaaaaaa..." started
    
    • 显示参数的前 10 字符,后接 ... 表示内容被截断。

  if (len) {
                ngx_memmove(b->start, start, len);
            }

缓冲区耗尽时:当 b->pos >= b->last 时,需要从文件中读取新数据。
未处理数据的存在:如果 len > 0,表示缓冲区中仍有未处理的数据(如跨缓冲区的 token)。

ngx_memmove(b->start, start, len);
作用:将未处理数据(从 start 开始,长度为 len)移动到缓冲区的起始位置(b->start)。
目的:腾出缓冲区剩余空间,以便读取新数据并拼接到未处理数据后。


size = (ssize_t) (file_size - cf->conf_file->file.offset);

计算文件中剩余未读取的字节数


限制读取大小
if (size > b->end - (b->start + len)) {
    size = b->end - (b->start + len);
}
  • 作用:确保读取的字节数不超过缓冲区的剩余空间。
  • 计算
    • b->end:缓冲区的末尾地址。
    • b->start + len:缓冲区中已迁移数据后的末尾地址。
    • b->end - (b->start + len):缓冲区剩余可用空间的大小。

读取文件数据
n = ngx_read_file(&cf->conf_file->file, b->start + len, size,
                 cf->conf_file->file.offset);
  • 作用:从文件中读取数据到缓冲区。
  • 参数
    • &cf->conf_file->file:文件结构体指针。
    • b->start + len:缓冲区中写入数据的起始位置(紧接已迁移数据后)。
    • size:实际读取的字节数(受缓冲区剩余空间限制)。
    • cf->conf_file->file.offset:文件的当前读取位置。

检查读取失败
if (n == NGX_ERROR) {
    return NGX_ERROR;
}
  • 作用:检测 ngx_read_file 是否返回错误(如文件不可读、权限问题)。
  • 处理:直接返回 NGX_ERROR,终止解析流程。

检查不完整读取
if (n != size) {
    ngx_conf_log_error(...);
    return NGX_ERROR;
}
  • 作用:验证实际读取的字节数(n)是否等于预期字节数(size)。
  • 触发条件
    • 文件被截断(如磁盘空间不足导致写入不完整)。
    • 磁盘 I/O 错误(如硬件故障导致部分读取)。
    • 文件被其他进程修改(读取过程中文件被缩短)。

b->pos = b->start + len;
b->last = b->pos + n;
start = b->start;

if (dump) {
    dump->last = ngx_cpymem(dump->last, b->pos, size);
}

缓冲区指针更新

b->pos = b->start + len;
  • 作用:将 pos 指针指向缓冲区中未处理数据的起始位置。
  • 背景
    • len 是迁移后的未处理数据长度(如跨缓冲区的 token 部分)。
    • b->start 是缓冲区的起始地址。

b->last = b->pos + n;
  • 作用:更新 last 指针,标记缓冲区中有效数据的末尾。
  • 变量
    • n 是实际读取的字节数(通过 ngx_read_file 返回)。

start = b->start;

作用:重置 start 指针,指向缓冲区的起始位置。

为下一个 token 的解析做准备。


数据转储(Dump)

if (dump)
  • 条件dump 是一个可选的缓冲区,用于保存原始配置内容(如调试或生成配置快照)。
  • 典型用途
    • 生成配置文件的完整副本(如 nginx -t 测试配置时输出)。
    • 调试时记录解析过程中的原始数据。

ngx_cpymem 操作
dump->last = ngx_cpymem(dump->last, b->pos, size);
  • 作用:将新读取的数据(b->posb->pos + size)追加到 dump 缓冲区。
  • 参数
    • dump->lastdump 缓冲区当前的写入位置。
    • b->pos:新数据的起始地址。
    • size:实际读取的字节数(与 n 相同)。

  • 返回值
    • ngx_cpymem 返回新的 dump->last 位置(原位置 + size)。

  • 转储同步
    • dump 缓冲区实时保存所有读取的数据,生成配置文件的完整镜像。

字符读取与指针移动
ch = *b->pos++;

从缓冲区 b 的当前指针位置(b->pos)读取一个字符,并将指针后移一位。
实现逐字符解析,确保每个字符都被处理。


换行符处理
if (ch == LF) {
    cf->conf_file->line++;  // 行号递增

    if (sharp_comment) {  // 结束行注释
        sharp_comment = 0;
    }
}
  • 作用
    • 遇到换行符(\n)时,行号(cf->conf_file->line)递增,用于错误定位。
    • 如果当前处于行注释状态(sharp_comment 为真),则结束注释。
  • 意义
    • 行号跟踪:确保错误信息能精确到具体行(如 line 10)。
    • 注释结束:行注释(# 开始的注释)在换行符处自动终止。

sharp_comment 是一个状态标志,用于标识解析器当前是否处于行注释(以 # 开头的注释)的上下文中


注释跳过
if (sharp_comment) {
    continue;
}

作用:如果处于行注释状态(sharp_comment = 1),跳过后续处理,直接进入下一次循环。

避免处理注释中的无效字符。


if (quoted) {
    quoted = 0;
    continue;
}
  • 作用:如果处于转义状态(quoted = 1,即前一个字符是 \),则取消转义状态,并跳过当前字符的常规处理。

http://www.kler.cn/a/573467.html

相关文章:

  • Camera相关配置
  • 谷歌自研AI大模型Gemini 2.0介绍以及API调用方法
  • idea中使用DeepSeek让编程更加便捷
  • 用于管理 Elasticsearch Serverless 项目的 AI Agent
  • Visual Studio Code (VSCode) 使用 GDB 进行调试
  • 【前端】【vue-i8n】【element】Element 框架国际化配置指南:从 element-ui 到 element-plus
  • Diffusion模型中时间t嵌入的方法
  • MapReduce 深度解析:原理与案例实战
  • 7.RabbitMQ延时交换机
  • 分布式日志和责任链路
  • FFmpeg-chapter7-使用 FFmpeg 解码视频(原理篇)
  • Day02-云服务器+小皮phpstudy一键部署建站
  • STM32中的ADC
  • 带你从入门到精通——自然语言处理(五. 自注意力机制和transformer的输入部分)
  • Element Plus中的树组件的具体用法(持续更新!)
  • Logback:高性能日志框架完全指南
  • 微信小程序接入deepseek
  • 【文生图】windows 部署stable-diffusion-webui
  • 数组中的逆序对(C++)
  • BasicToolNode(tools=[search_tool, lookup_policy, query_sqldb])的内部执行逻辑