1 定义ngx_signal_worker_processes 函数 定义在 ./nginx-1.24.0/src/os/unix/ngx_process_cycle.cstaticvoidngx_signal_worker_processes(ngx_cycle_t*cycle,intsigno){ngx_int_ti;ngx_err_terr;ngx_channel_tch;ngx_memzero(ch,sizeof(ngx_channel_t));#if(NGX_BROKEN_SCM_RIGHTS)ch.command0;#elseswitch(signo){casengx_signal_value(NGX_SHUTDOWN_SIGNAL):ch.commandNGX_CMD_QUIT;break;casengx_signal_value(NGX_TERMINATE_SIGNAL):ch.commandNGX_CMD_TERMINATE;break;casengx_signal_value(NGX_REOPEN_SIGNAL):ch.commandNGX_CMD_REOPEN;break;default:ch.command0;}#endifch.fd-1;for(i0;ingx_last_process;i){ngx_log_debug7(NGX_LOG_DEBUG_EVENT,cycle-log,0,child: %i %P e:%d t:%d d:%d r:%d j:%d,i,ngx_processes[i].pid,ngx_processes[i].exiting,ngx_processes[i].exited,ngx_processes[i].detached,ngx_processes[i].respawn,ngx_processes[i].just_spawn);if(ngx_processes[i].detached||ngx_processes[i].pid-1){continue;}if(ngx_processes[i].just_spawn){ngx_processes[i].just_spawn0;continue;}if(ngx_processes[i].exitingsignongx_signal_value(NGX_SHUTDOWN_SIGNAL)){continue;}if(ch.command){if(ngx_write_channel(ngx_processes[i].channel[0],ch,sizeof(ngx_channel_t),cycle-log)NGX_OK){if(signo!ngx_signal_value(NGX_REOPEN_SIGNAL)){ngx_processes[i].exiting1;}continue;}}ngx_log_debug2(NGX_LOG_DEBUG_CORE,cycle-log,0,kill (%P, %d),ngx_processes[i].pid,signo);if(kill(ngx_processes[i].pid,signo)-1){errngx_errno;ngx_log_error(NGX_LOG_ALERT,cycle-log,err,kill(%P, %d) failed,ngx_processes[i].pid,signo);if(errNGX_ESRCH){ngx_processes[i].exited1;ngx_processes[i].exiting0;ngx_reap1;}continue;}if(signo!ngx_signal_value(NGX_REOPEN_SIGNAL)){ngx_processes[i].exiting1;}}}ngx_signal_worker_processes 函数是 Nginx 主进程中用于向所有工作进程及辅助进程分发信号的函数。 它优先通过进程间通道发送优雅的控制命令如退出、重新打开日志 在通道不可用或失败时则使用 kill 系统调用直接发送信号 并根据发送结果更新进程的退出状态。2 详解1 函数签名staticvoidngx_signal_worker_processes(ngx_cycle_t*cycle,intsigno)返回值 函数不返回任何值参数1 ngx_cycle_t *cycle 当前运行周期上下文环境参数2 int signo 表示要发送的信号编号2 逻辑流程1 命令准备 1 不支持传递文件描述符命令置为 0表示没有有效命令 2 支持传递文件描述符根据输入信号设置对应的命令 2 遍历所有进程发送命令 1 跳过无需发送命令的进程 2 命令有效通过通道发送命令 3 命令无效或通道发送命令失败通过 kill 发送信号1 命令准备{ngx_int_ti;ngx_err_terr;ngx_channel_tch;局部变量声明ngx_memzero(ch,sizeof(ngx_channel_t));将 ch 结构体所有字段清零若系统不支持通过通道传递文件描述符 定义了 NGX_BROKEN_SCM_RIGHTS 则编译以下代码块。#if(NGX_BROKEN_SCM_RIGHTS)ch.command0;直接将通道命令置为 0表示没有有效命令。 逻辑 既然系统不支持通道传递描述符 那么通道命令机制也不可用 因此强制命令为 0后续将跳过通道发送只使用 kill 信号。支持传递文件描述符#elseswitch(signo){casengx_signal_value(NGX_SHUTDOWN_SIGNAL):ch.commandNGX_CMD_QUIT;break;casengx_signal_value(NGX_TERMINATE_SIGNAL):ch.commandNGX_CMD_TERMINATE;break;casengx_signal_value(NGX_REOPEN_SIGNAL):ch.commandNGX_CMD_REOPEN;break;default:ch.command0;}#endif根据输入信号选择对应的通道命令优雅关闭信号通常为 SIGQUIT 通道命令设为 NGX_CMD_QUIT 工作进程收到后会优雅关闭。强制终止信号通常为 SIGINT 或 SIGTERM 通道命令设为 NGX_CMD_TERMINATE 工作进程收到后快速退出。重新打开日志文件信号通常为 SIGUSR1 通道命令设为 NGX_CMD_REOPEN 工作进程收到后重新打开日志文件。其他信号 不设置命令0 表示无效 因为其他信号不需要通过通道传递特殊语义。ch.fd-1;将通道消息中的文件描述符字段设为 -1无效值。 本次通知不传递任何文件描述符显式标记为无效接收方应忽略该字段。2 遍历所有进程发送命令for(i0;ingx_last_process;i){遍历全局进程数组 ngx_processes。 ngx_last_process 是当前管理的进程总数。 对每一个记录在案的进程尝试发送信号。ngx_log_debug7(NGX_LOG_DEBUG_EVENT,cycle-log,0,child: %i %P e:%d t:%d d:%d r:%d j:%d,i,ngx_processes[i].pid,ngx_processes[i].exiting,ngx_processes[i].exited,ngx_processes[i].detached,ngx_processes[i].respawn,ngx_processes[i].just_spawn);输出当前进程的详细状态到调试日志if(ngx_processes[i].detached||ngx_processes[i].pid-1){continue;}过滤已分离或 PID 无效的进程。 分离的进程已脱离主进程管理 PID 为 -1 表示进程不存在。 这两种情况均无需也无法发送信号直接跳过。 避免向无效目标发送信号if(ngx_processes[i].just_spawn){ngx_processes[i].just_spawn0;continue;}跳过刚刚生成fork的进程并清除该标志。 刚 fork 出的进程可能还未完成信号处理函数的设置 立即发送信号可能造成竞态或未定义行为。 跳过一次下次调用本函数时标志已清除可以正常发送。if(ngx_processes[i].exitingsignongx_signal_value(NGX_SHUTDOWN_SIGNAL)){continue;}避免对已经在优雅关闭的进程重复发送 SIGQUIT。 如果进程已标记为 exiting说明已收到退出指令且本次信号仍是优雅关闭则无需再次通知。 但若本次是强制终止等信号则仍会穿透此检查继续处理因为可能需要强制结束卡住的进程。 意义减少不必要的信号发送同时允许强制信号覆盖正在优雅退出的进程。if(ch.command){if(ngx_write_channel(ngx_processes[i].channel[0],ch,sizeof(ngx_channel_t),cycle-log)NGX_OK){if(signo!ngx_signal_value(NGX_REOPEN_SIGNAL)){ngx_processes[i].exiting1;}continue;}}检查通道命令是否有效非零。 如果有效优先尝试通过进程间通道发送命令 这比直接 kill 更优雅。调用 ngx_write_channel 将封装好的命令消息通过 socket 发送给目标进程。 参数 ngx_processes[i].channel[0] 是主进程端与该进程通信的 socket 文件描述符 ch 为消息内容 cycle-log 用于记录错误。 返回值成功返回 NGX_OK。若通道发送成功且信号不是重新打开日志NGX_REOPEN_SIGNAL 则将进程状态标记为 exiting 1正在退出。 然后 continue 跳过 kill 操作处理下一个进程。 重新打开日志并不导致进程退出因此不设 exiting 标志。 其他信号关闭、终止都会令进程最终退出设置标志以跟踪其退出状态。 意义精确维护进程生命周期状态为后续回收子进程提供依据。ngx_log_debug2(NGX_LOG_DEBUG_CORE,cycle-log,0,kill (%P, %d),ngx_processes[i].pid,signo);记录即将对该 PID 使用 kill 发送信号的调试日志。 当通道命令不可用或发送失败时会走到这里使用传统信号方式if(kill(ngx_processes[i].pid,signo)-1){errngx_errno;ngx_log_error(NGX_LOG_ALERT,cycle-log,err,kill(%P, %d) failed,ngx_processes[i].pid,signo);if(errNGX_ESRCH){ngx_processes[i].exited1;ngx_processes[i].exiting0;ngx_reap1;}continue;}调用 kill 系统调用向目标进程发送信号。 直接发送原始信号值 signo。 成功返回 0失败返回 -1 并设置 errno。 意义作为通道不可用或失败时的后备方案保证信号最终能被传递。若 kill 失败保存错误码并记录 ALERT 级别日志包含 PID 和信号编号。如果错误原因是 ESRCH进程不存在 则更新进程状态为已退出 (exited 1) 清除 exiting 标志并设置全局 ngx_reap 1通知主事件循环进行回收。 逻辑进程已意外终止主进程需要尽快获知并清理子进程资源。 意义自动处理子进程异常退出避免僵尸进程保证进程表状态准确。kill 失败后跳过后续状态设置因为进程未收到信号 继续处理下一个进程。if(signo!ngx_signal_value(NGX_REOPEN_SIGNAL)){ngx_processes[i].exiting1;}}}如果 kill 发送成功且信号不是重新打开日志 则同样将进程标记为 exiting 1。 与通道发送成功时的状态更新逻辑完全一致保持行为统一。 意义无论通过哪种方式发送退出信号都能正确记录进程即将退出的状态。
网站建设
高端定制
企业官网