1 定义 ngx_worker_process_cycle 函数 定义在 ./nginx-1.24.0/src/os/unix/ngx_process_cycle.cstatic void ngx_worker_process_cycle ( ngx_cycle_t * cycle, void * data) { ngx_int_t worker= ( intptr_t ) data; ngx_process= NGX_PROCESS_WORKER; ngx_worker= worker; ngx_worker_process_init ( cycle, worker) ; ngx_setproctitle ( "worker process" ) ; for ( ; ; ) { if ( ngx_exiting) { if ( ngx_event_no_timers_left ( ) == NGX_OK) { ngx_log_error ( NGX_LOG_NOTICE, cycle-> log, 0 , "exiting" ) ; ngx_worker_process_exit ( cycle) ; } } ngx_log_debug0 ( NGX_LOG_DEBUG_EVENT, cycle-> log, 0 , "worker cycle" ) ; ngx_process_events_and_timers ( cycle) ; if ( ngx_terminate) { ngx_log_error ( NGX_LOG_NOTICE, cycle-> log, 0 , "exiting" ) ; ngx_worker_process_exit ( cycle) ; } if ( ngx_quit) { ngx_quit= 0 ; ngx_log_error ( NGX_LOG_NOTICE, cycle-> log, 0 , "gracefully shutting down" ) ; ngx_setproctitle ( "worker process is shutting down" ) ; if ( ! ngx_exiting) { ngx_exiting= 1 ; ngx_set_shutdown_timer ( cycle) ; ngx_close_listening_sockets ( cycle) ; ngx_close_idle_connections ( cycle) ; ngx_event_process_posted ( cycle, & ngx_posted_events) ; } } if ( ngx_reopen) { ngx_reopen= 0 ; ngx_log_error ( NGX_LOG_NOTICE, cycle-> log, 0 , "reopening logs" ) ; ngx_reopen_files ( cycle, - 1 ) ; } } } ngx_worker_process_cycle 是 Nginx worker 进程的主循环函数, 核心作用如下: 初始化 worker 进程:设置进程身份、调用模块初始化、设置进程标题。 进入事件驱动循环: 反复调用 ngx_process_events_and_timers 处理网络 I/O 事件和定时器事件,是请求处理的核心驱动。 响应信号实现进程控制: SIGQUIT:触发优雅关闭(关闭监听、等待现有请求完成后再退出)。 SIGTERM:立即强制退出。 SIGUSR1:重新打开日志文件,支持日志轮转。 管理 worker 退出:在优雅关闭模式下,监测连接与定时器状态,确保安全退出。2 详解 1 函数签名 static void ngx_worker_process_cycle ( ngx_cycle_t * cycle, void * data) 返回值 函数不返回任何值 worker 进程的主循环是一个永不返回的无限循环(for (;;)), 只有在进程退出时才会通过调用 ngx_worker_process_exit() 等函数直接终止进程。 因此该函数不需要向调用者返回执行结果。参数 ngx_cycle_t *cycle 指向当前运行周期上下文 void *data worker 进程的序号(从 0 开始),以 intptr_t 整数类型强制转换为 void* 传入 通过 void* 可以传递任意类型的数据(结构体指针、整数等),增强了接口的通用性2 逻辑流程 1 局部变量 2 全局变量 3 初始化 4 修改进程标题 5 事件循环1 局部变量{ ngx_int_t worker= ( intptr_t ) data; 从通用指针参数 data 中提取出 worker 进程的序号, 并赋值给局部变量 worker2 全局变量ngx_process= NGX_PROCESS_WORKER; ngx_worker= worker; #1 设置全局变量 ngx_process 的值,标记当前进程的类型为 Worker NGX_PROCESS_MASTER:master 进程。 NGX_PROCESS_WORKER:worker 进程。 NGX_PROCESS_SINGLE:单进程模式。 #2 设置全局变量 ngx_worker 的值,保存当前 worker 的编号为何需要同时设置类型和编号? 类型 (ngx_process):回答“我是哪类进程?”—— 宏观角色。 编号 (ngx_worker):回答“我是该类进程中的哪一个?”—— 微观实例。 二者结合完整描述了一个进程在整个 Nginx 多进程架构中的坐标3 初始化ngx_worker_process_init ( cycle, worker) ; Worker 进程启动后执行的第一个核心初始化函数。 它的作用是为 Worker 进程构建一个独立、完整且可运行的环境,使其能够处理请求。 如果没有这一步,Worker 进程就无法接入 Nginx 的事件驱动架构。4 修改进程标题ngx_setproctitle ( "worker process" ) ; 修改了进程在系统中显示的名称 提升可观测性 优化调试体验5 事件循环for ( ; ; ) { Worker 进程的事件循环主循环。 Worker 进程完成初始化后,便进入这个无限循环,它交替执行两大任务: 处理网络 I/O 与定时器事件,以及响应 Master 进程发来的信号指令。if ( ngx_exiting) { if ( ngx_event_no_timers_left ( ) == NGX_OK) { ngx_log_error ( NGX_LOG_NOTICE, cycle-> log, 0 , "exiting" ) ; ngx_worker_process_exit ( cycle) ; } } #1 检查全局标志位 ngx_exiting 是否为真 ngx_exiting 是整个优雅退出的总开关。一旦被置为 1, 意味着: 该 Worker 已经不再监听任何端口(ngx_close_listening_sockets 已执行)。 空闲连接已被主动关闭(ngx_close_idle_connections 已执行)。 当前正处于等待现有活跃连接自然结束的阶段。 循环位置的意义: 该检查放在每次事件循环的最开始, 保证了 Worker 在完成上一轮事件处理后, 会立即评估是否具备退出条件,避免不必要的阻塞等待。#2 if (ngx_event_no_timers_left() == NGX_OK) 函数检查事件模块中维护的定时器红黑树是否为空 若定时器树为空(没有任何节点),返回 NGX_OK。 若定时器树中仍有节点(说明还有连接在等待超时事件),返回 NGX_ERROR。 定时器与连接的关系: 在 Nginx 中,几乎每一个活跃的连接都对应一个定时器事件。 退出条件判断逻辑:只有当定时器树彻底为空时,才能断定: 所有连接要么已被正常关闭,要么已因超时被处理完毕。 没有任何连接处于“等待未来某个时刻执行超时回调”的状态。 此刻退出进程完全安全,不会中断任何尚未完成的请求。#3 日志记录与退出 ngx_log_error:在错误日志中写入 [notice] 级别的 "exiting" 消息。 运维人员可以通过日志确认 Worker 是正常优雅退出的,而非异常崩溃。 ngx_worker_process_exit(cycle): 执行 Worker 进程的最终退出动作:ngx_log_debug0 ( NGX_LOG_DEBUG_EVENT, cycle-> log, 0 , "worker cycle" ) ; 记录日志ngx_process_events_and_timers ( cycle) ; Worker 进程主循环中的核心调度器, 它负责在一次循环迭代中,统一调度和分发网络 I/O 事件与定时器事件if ( ngx_terminate) { ngx_log_error ( NGX_LOG_NOTICE, cycle-> log, 0 , "exiting" ) ; ngx_worker_process_exit ( cycle) ; } #1 if (ngx_terminate) ngx_terminate:全局整型标志变量,初始值为 0。 置位时机: 当 Worker 进程接收到 SIGTERM 或 SIGINT 信号时, 信号处理函数 ngx_signal_handler() 会将该标志置为 1。 检查时机: 每次事件循环完成一轮 I/O 和定时器处理后,立即检查该标志。#2 ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting"); 向 Nginx 错误日志文件写入一条 [notice] 级别的日志,内容为 exiting。 运维人员可通过日志确认 Worker 进程是由于收到 SIGTERM 而退出的,而非异常崩溃。#3 ngx_worker_process_exit(cycle); 调用 Worker 进程的退出函数,执行以下关键步骤: 调用所有模块的 exit_process 回调函数,释放模块级资源。 销毁内存池 cycle->pool,归还所有分配的内存。 关闭所有打开的文件描述符。 最终调用系统调用 exit(0) 或 _exit(0) 终止进程。if ( ngx_quit) { ngx_quit= 0 ; ngx_log_error ( NGX_LOG_NOTICE, cycle-> log, 0 , "gracefully shutting down" ) ; ngx_setproctitle ( "worker process is shutting down" ) ; if ( ! ngx_exiting) { ngx_exiting= 1 ; ngx_set_shutdown_timer ( cycle) ; ngx_close_listening_sockets ( cycle) ; ngx_close_idle_connections ( cycle) ; ngx_event_process_posted ( cycle, & ngx_posted_events) ; } } Nginx 优雅关闭机制的启动触发器。 当 Worker 进程收到 SIGQUIT 信号后, 该段逻辑会执行一次性的状态切换和资源清理动作, 将进程从“正常服务”状态切换为“待退出”状态。#1 第一层条件:if (ngx_quit) ngx_quit:全局标志变量,初始值为 0。 置位时机: 当 Worker 进程接收到 SIGQUIT 信号时,信号处理函数 ngx_signal_handler() 将其置为 1。 检查时机: 在完成本轮 I/O 事件和定时器处理后,且在 ngx_terminate 检查和 ngx_reopen 检查之间。 注意:该标志仅用于触发优雅关闭的启动动作,执行一次后立即被清零, 因此该 if 块在整个进程生命周期中最多执行一次。#2 ngx_quit = 0; 作用:立即将 ngx_quit 标志清零。 逻辑:防止后续循环迭代重复进入该 if 块,确保优雅关闭的初始化动作只执行一次。 意义:这是一种常见的“一次性触发”模式,将异步信号转化为一次同步的状态转换。#3 日志与进程标题更新 日志记录: 向错误日志写入 [notice] 级别的 "gracefully shutting down", 明确记录 Worker 进程开始优雅关闭流程。 进程标题: 调用 ngx_setproctitle 修改进程名称为 "worker process is shutting down"。 运维人员通过 ps aux | grep nginx 可直观看到哪些 Worker 正在关闭中,提升可观测性。 意义:为运维提供了明确的状态可视化,便于监控和问题追踪。#4 if (!ngx_exiting) ngx_exiting: 全局标志,表示进程已经进入待退出状态(即优雅关闭流程已启动)。 检查目的: 确保接下来的清理动作只执行一次。 由于 ngx_quit 被清零,ngx_exiting 成为防止重复执行的第二道保险。#5 ngx_exiting = 1; 作用:将 ngx_exiting 标志置为 1,正式宣告进程进入“待退出状态”。 联动效果:从下一轮循环开始,位于循环头部的 if (ngx_exiting) 检查将每轮执行, 持续判断定时器树是否为空,直至最终退出。 意义:这是优雅关闭流程中的状态切换枢纽, 将一次性启动动作与持续性的退出条件检查分离开来。#6 ngx_set_shutdown_timer(cycle); 作用:设置一个全局保底定时器。 逻辑:该函数通常为空操作,但某些模块(如 ngx_event_core_module)可能会设置一个超时时间 如果 Worker 在指定时间内未能自然退出(例如某些连接异常僵死),该定时器超时后会强制终止进程。 意义:防止因某些异常连接或 Bug 导致 Worker 永远无法退出,确保优雅关闭流程终将结束。#7 ngx_close_listening_sockets(cycle); 作用:关闭当前 Worker 进程所有的监听套接字。 逻辑: 遍历 cycle->listening 数组, 对每个监听套接字调用 close() 系统调用,并从事件模块(如 epoll)中移除相关事件。 后果: 该 Worker 从此不再接受任何新的客户端连接请求。 意义: 这是优雅关闭的核心隔离动作。它切断了新请求的入口, 使 Worker 进入“只出不进”的静默状态,将资源专注于处理现有连接。#8 ngx_close_idle_connections(cycle); 作用: 遍历连接池,主动关闭当前空闲的 Keep-Alive 连接。 空闲连接定义: 指那些已经完成了一次请求-响应交互、正等待下一次请求的 Keep-Alive 连接 逻辑: 对这些连接调用 ngx_close_connection(),立即释放资源。 不关闭活跃连接: 正在处理请求(有读/写事件挂起)的连接不会被关闭,它们将继续由事件循环处理直至完成。#9 ngx_event_process_posted(cycle, &ngx_posted_events); 作用: 立即处理 ngx_posted_events 延迟事件队列。 背景: 在 ngx_process_events_and_timers 中, 一些优先级较低的事件会被放入 ngx_posted_events 队列, 通常在当前循环末尾统一处理。 此时主动调用该函数, 是为了在开始等待退出之前,尽快处理掉这些积压事件,推动连接状态向前演变。 意义:加速现有请求的处理进程,使 Worker 能更快地满足最终退出条件。完整流程串联: 从 SIGQUIT 到最终退出 信号到达 → ngx_quit = 1(异步信号处理函数设置)。 主循环检测 → 进入 if (ngx_quit) 块,执行上述初始化动作: 清零 ngx_quit,更新日志和进程标题。 设置 ngx_exiting = 1,启动保底定时器。 关闭监听端口,关闭空闲连接。 立即处理延迟事件队列。 状态转换完成 → Worker 进入“待退出状态”。 后续循环 → 循环头部的 if (ngx_exiting) 检查每轮执行,等待定时器树清空。 最终退出 → 定时器树为空时,执行 ngx_worker_process_exit。if ( ngx_reopen) { ngx_reopen= 0 ; ngx_log_error ( NGX_LOG_NOTICE, cycle-> log, 0 , "reopening logs" ) ; ngx_reopen_files ( cycle, - 1 ) ; } } } #1 if (ngx_reopen) ngx_reopen:全局标志变量,初始值为 0。 置位时机: 当 Worker 进程接收到 SIGUSR1 信号时,信号处理函数 ngx_signal_handler() 会将其置为 1。 检查时机:在每次事件循环末尾,处理完 ngx_quit 逻辑之后。#2 ngx_reopen = 0; 作用: 立即将 ngx_reopen 标志清零。 逻辑: 防止在下一次循环迭代时重复执行日志重开操作,确保一次信号只触发一次实际的 reopen 动作。#3 ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs"); 作用: 向错误日志文件写入一条 [notice] 级别的日志,内容为 "reopening logs"。 注意: 此时写入日志使用的是旧的文件描述符,因为重新打开的动作尚未执行。 该日志会出现在日志切割前的旧日志文件中,方便运维人员追踪操作时间线。 意义: 为运维操作留下审计记录,确认信号已被 Worker 进程接收并处理。#4 ngx_reopen_files(cycle, -1); 函数原型: void ngx_reopen_files(ngx_cycle_t *cycle, ngx_uint_t reopen_type); 参数说明: cycle: 全局核心结构体,包含所有需要重新打开的文件信息。 -1: 特殊标志值,表示 只重新打开日志文件,不重新打开监听套接字。 若传入正值,则表示在平滑升级场景中重新打开监听端口。