1 定义ngx_master_process_cycle 函数 定义在 ./nginx-1.24.0/src/os/unix/ngx_process_cycle.c2 作用ngx_master_process_cycle 是 Nginx 主进程的核心循环函数。 它负责 设置信号处理掩码、 启动 worker 和缓存管理进程 然后进入无限循环 通过 sigsuspend 等待信号 并根据全局标志位处理 子进程回收、服务停止、配置热重载、日志滚动等管理任务。核心任务包括 1 管理子进程 启动、监控和重启 worker 进程 2 信号处理 阻塞并响应各种信号执行相应操作。 3 进程生命周期控制 根据信号或子进程状态执行配置重新加载、优雅停机或强制终止 并在子进程异常退出时重新生成。 4 保持 master 持续运行 循环处理直到所有子进程退出且收到退出信号为止。3 详解1 函数签名voidngx_master_process_cycle(ngx_cycle_t*cycle)返回值 返回类型void 函数不返回任何值。 一旦进入该函数主进程将陷入无限循环直到收到终止信号退出整个进程 因此从调用者的角度看这个函数永远不会正常返回函数名 ngx_master_process_cycle 命名空间前缀 ngx_ Nginx 函数的标准前缀 master_process 明确指出该函数仅在 master 进程主进程中运行。 Nginx 在启动时根据配置和进程模式 会调用 ngx_master_process_cycle 或 ngx_single_process_cycle 分支。 cycle周期 cycle 表明其本质是 阻塞式控制主循环而非一次性初始化函数。 cycle 强调了这个函数将在给定的运行周期中不断循环处理管理事件。 整体语义 函数名准确传达了它的角色—— 在 master 进程的运行上下文中进入无限事件循环。 Master 进程的大脑。 不负责网络 I/O 专职进程调度、信号路由、配置热重载、平滑升级与优雅退出管控。 是 Nginx 高可用架构的调度中枢。参数 ngx_cycle_t *cycle 类型 指向 ngx_cycle_t 结构体的指针 这是 Nginx 全局运行环境的根对象 包含了所有核心数据配置树、模块上下文、连接池、监听 socket、日志对象、时间缓存等等。 在函数中的角色 作为 master 进程的运行时上下文 函数内部几乎所有操作都离不开 cycle。 唯一参数承载一切 Nginx 的设计哲学中cycle 是各个模块和进程访问全局资源的入口。 因此 ngx_master_process_cycle 只需要一个 cycle 指针就可以获取所有必要的信息。 设计意义 将整个运行环境作为参数传递而非依赖分散的全局变量使得代码更模块化 支持配置热重载 重载时会用新的 cycle 替换旧的 而旧的 cycle 会在所有引用释放后被安全销毁 主循环只需要更新局部变量 cycle 即可。总结 ngx_master_process_cycle 的函数签名 void 返回值表示函数将进入永久循环不会正常返回。 函数名清晰地表达了它的职责——在 master 进程的运行周期中进行循环管理。 唯一的 cycle 参数提供了主进程所需的全部运行时上下文体现了 Nginx 高度集中的环境管理策略。 这个签名是 Nginx 进程管理模型的核心入口 它将 main 函数的控制权转移给主进程的管理循环 从此 Nginx 开始正式服务并响应各种管理信号。2 逻辑流程1 信号屏蔽 2 进程标题修改 3 启动 worker 进程 4 master 进程循环{char*title;u_char*p;size_tsize;ngx_int_ti;ngx_uint_tsigio;sigset_tset;structitimervalitv;ngx_uint_tlive;ngx_msec_tdelay;ngx_core_conf_t*ccf;局部变量声明1 信号屏蔽sigemptyset(set);sigaddset(set,SIGCHLD);sigaddset(set,SIGALRM);sigaddset(set,SIGIO);sigaddset(set,SIGINT);sigaddset(set,ngx_signal_value(NGX_RECONFIGURE_SIGNAL));sigaddset(set,ngx_signal_value(NGX_REOPEN_SIGNAL));sigaddset(set,ngx_signal_value(NGX_NOACCEPT_SIGNAL));sigaddset(set,ngx_signal_value(NGX_TERMINATE_SIGNAL));sigaddset(set,ngx_signal_value(NGX_SHUTDOWN_SIGNAL));sigaddset(set,ngx_signal_value(NGX_CHANGEBIN_SIGNAL));if(sigprocmask(SIG_BLOCK,set,NULL)-1){ngx_log_error(NGX_LOG_ALERT,cycle-log,ngx_errno,sigprocmask() failed);}sigemptyset(set);整体设计意义 避免异步中断 Master 进程的核心工作是管理子进程和处理配置变更 这些操作必须原子化不能在任意代码位置被信号打断。 阻塞信号使得信号处理被延迟到主循环中的安全点。 经典的同步模式 sigprocmask 阻塞信号 → 主循环检查标志 → sigsuspend(empty_set) 挂起等待信号。 检查标志与挂起之间信号可能到达 集中管理 所有管理信号都通过全局标志如 ngx_reconfigure、ngx_reopen驱动 逻辑清晰且易于扩展。#1 将信号集 set 清空使其不包含任何信号。 必须先清空集合才能开始添加需要的信号。避免残留的未定义位影响后续操作。#2 将 4 个标准 POSIX 信号逐个加入集合 SIGCHLD SIGALRM SIGIO SIGINT#3 通过 Nginx 封装的宏 ngx_signal_value() 将 Nginx 内部统一信号编号 转换为系统实际的信号值 将逻辑信号名转换为实际操作系统信号编号并加入集合。 实现跨平台信号抽象。不同 OS 的信号编号可能不同 该宏在编译期自动映射保证核心逻辑与平台解耦。 #3-1 NGX_RECONFIGURE_SIGNAL → SIGHUP 热重载配置 当 master 收到此信号时 会重新读取配置文件、初始化新的 cycle、启动新的 worker 进程 并通知旧 worker 优雅退出实现热重载。 #3-2 NGX_REOPEN_SIGNAL → SIGUSR1 日志轮转 用户发送该信号后master 会重新打开自己的日志文件 并向所有 worker 进程发送同一信号使它们也能重新打开日志文件 #3-3 NGX_NOACCEPT_SIGNAL → SIGWINCH 停止接受连接 平滑升级或维护时指示 Worker 停止 accept() 向旧 master 发送此信号会让旧 worker 进程不再接受新连接 待现有连接处理完毕后自动退出从而实现无缝升级。 #3-4 NGX_TERMINATE_SIGNAL → SIGTERM 强制终止带超时倒计时超时未退出则发 SIGKILL 强杀 系统默认的终止信号。 Nginx 收到后master 会立即向所有子进程发送 SIGTERM 并等待一段延迟时间后若子进程仍未退出则发送 SIGKILL。用于快速停止服务 #3-5 NGX_SHUTDOWN_SIGNAL → SIGQUIT 优雅关闭Worker 处理完当前请求后自行退出 master 收到 SIGQUIT 后会向所有子进程发送 SIGQUIT让它们优雅地完成当前请求后退出 同时 master 关闭监听套接字并等待子进程全部结束最后自身退出。用于无损停机。 #3-6 NGX_CHANGEBIN_SIGNAL → SIGUSR2 二进制平滑升级Fork 出新 Master 进程继承监听套接字实现零停机升级 当需要升级 Nginx 可执行文件时向 master 发送 SIGUSR2。 master 会执行新版本的二进制程序通过 ngx_exec_new_binary 新进程启动后接管监听端口旧 master 继续运行并管理旧 worker 进程最终通过 SIGWINCH 完成切换。 这是 Nginx 热升级的核心机制。#4 将前面构造的信号集设置为进程的信号掩码阻塞这些信号sigprocmask(SIG_BLOCK, set, NULL) 系统调用 sigprocmask 用于检查或更改当前进程的信号掩码即被阻塞的信号集合。 被阻塞的信号将不会被进程立即处理而是处于挂起状态直到解除阻塞。 第一个参数 SIG_BLOCK 指定操作类型。 SIG_BLOCK 表示将第二个参数指向的信号集添加到当前信号掩码中即阻塞这些信号。 其他可能的值包括 SIG_SETMASK直接设置掩码和 SIG_UNBLOCK解除阻塞。 第二个参数 set 指向之前填充好的信号集。 即需要被阻塞的信号 第三个参数 NULL 通常用于保存旧的信号掩码。 这里传入 NULL 表示不需要保存旧掩码因为后续不会用到。 调用后上述所有信号都被加入到 master 进程的阻塞集中。 此时如果这些信号被发送给 master 进程 它们将不会立即触发信号处理函数而是被内核挂起 直到后续解除阻塞通过 sigsuspend 或 sigprocmask(SIG_UNBLOCK)时才会递送。#5 sigemptyset(set); 清空 信号集合以备后续使用2 进程标题修改sizesizeof(master_process);for(i0;ingx_argc;i){sizengx_strlen(ngx_argv[i])1;}计算存储进程标题所需的内存大小 总长度 固定前缀长度 ∑(每个启动参数的长度 1个空格分隔符) 该结果将直接用于下一步 ngx_pnalloc() 的内存分配请求。titlengx_pnalloc(cycle-pool,size);if(titleNULL){/* fatal */exit(2);}分配进程标题所需的内存pngx_cpymem(title,master_process,sizeof(master_process)-1);for(i0;ingx_argc;i){*p ;pngx_cpystrn(p,(u_char*)ngx_argv[i],size);}将进程标题的基础字符串和所有命令行参数拼接到 title 缓冲区中ngx_setproctitle(title);将当前 master 进程在系统进程列表如 ps 命令中显示的标题 修改为之前构造好的、包含详细启动参数的自定义字符串3 启动 worker 进程ccf(ngx_core_conf_t*)ngx_get_conf(cycle-conf_ctx,ngx_core_module);ngx_start_worker_processes(cycle,ccf-worker_processes,NGX_PROCESS_RESPAWN);ngx_start_cache_manager_processes(cycle,0);读取核心配置并启动 worker 进程与缓存管理进程 完成 master 进程对子进程的初始创建#1 读取核心配置 获取 ngx_core_module 模块的配置 获取核心配置后master 进程才能知道应该启动多少个 worker 进程 以及后续操作中需要的其他核心参数。#2 启动指定数量的 worker 子进程并设置 master 对它们的监控策略 参数 cycle 当前周期对象传递给子进程使用。 ccf-worker_processes worker 进程的数量例如配置 worker_processes 4; 则值为 4。 NGX_PROCESS_RESPAWN 标志位表示这些 worker 进程在异常退出时 master 应自动重新生成respawn新的进程来替代。ngx_start_worker_processes#3 启动缓存管理进程。 缓存管理进程不参与网络请求处理 它们专门执行缓存文件的管理任务例如过期缓存清理、缓存加载等。 参数 cycle 当前周期对象。 0 第二个参数 0 表示“非重载启动” 在初次启动时会根据配置创建缓存管理进程。 意义将缓存管理工作从 worker 进程中剥离出来 避免影响 worker 处理请求的性能 同时集中管理缓存提高效率。 如果没有配置任何缓存 该函数不会创建任何进程调用无害。4 master 进程循环ngx_new_binary0;delay0;sigio0;live1;初始化 master 进程主循环的状态变量。 它们各自控制着 master 进程的终止流程、热升级状态以及子进程存活检测。作用将“正在执行二进制热升级”的标志置为假。 逻辑这是一个全局变量当 master 接收到 SIGUSR2NGX_CHANGEBIN_SIGNAL 并成功启动新版本的 master 进程后会将其设为 1。 此处初始化为 0表示当前未处于热升级流程中。作用将“终止延迟时间”清零。 逻辑delay 用于在强制终止ngx_terminate时 逐步升级信号的延迟策略。 当 master 需要立即停止所有 worker 进程时 首先向它们发送 SIGTERM并设置一个初始延迟如 50ms。 如果在延迟后仍有 worker 未退出会再次发送信号并将延迟翻倍 最终可能发送 SIGKILL。 此处初始化为 0表示尚未进入任何终止等待。作用将“终止信号发送计数器”清零。 逻辑sigio 用于在 ngx_terminate 分支中控制信号的发送节奏。 它记录还需要等待多少个 SIGIO或可理解为还需要进行几轮“信号发送 — 等待 — 再检查”。 初始为 0表示未处于任何需要计数等待的阶段。 当开始终止时它会被设置为 worker_processes 2 然后每次 sigsuspend 返回后递减直到降为 0 才再次发送信号 从而实现了“等待所有 worker 响应后再发下一轮信号”的效果。作用将“是否有存活的子进程”标志设为真1。 逻辑live 用于在每次 sigsuspend 后判断是否需要继续运行。 如果 live 变为 0 且同时 ngx_terminate 或 ngx_quit 为真 master 会调用 ngx_master_process_exit 退出。 此处初始化为 1是因为刚刚启动了 worker 和 cache 进程必定有子进程存活。 后续 ngx_reap_children 会根据实际收割情况更新 live0 表示所有子进程均已退出。 意义正确反映了当前系统中的子进程状态使得退出逻辑能够准确判断何时安全退出。这些变量会在循环中被修改控制 master 的行为。master 进程的主循环永不主动退出for(;;){if(delay){if(ngx_sigalrm){sigio0;delay*2;ngx_sigalrm0;}ngx_log_debug1(NGX_LOG_DEBUG_EVENT,cycle-log,0,termination cycle: %M,delay);itv.it_interval.tv_sec0;itv.it_interval.tv_usec0;itv.it_value.tv_secdelay/1000;itv.it_value.tv_usec(delay%1000)*1000;if(setitimer(ITIMER_REAL,itv,NULL)-1){ngx_log_error(NGX_LOG_ALERT,cycle-log,ngx_errno,setitimer() failed);}}ngx_log_debug0(NGX_LOG_DEBUG_EVENT,cycle-log,0,sigsuspend);sigsuspend(set);sigsuspend(set); 系统调用 参数是要阻塞的信号 传入空集表示临时解除所有信号的阻塞 挂起进程直到收到任意信号 信号处理函数返回后sigsuspend 将进程的信号掩码恢复为调用前的值ngx_time_update();ngx_log_debug1(NGX_LOG_DEBUG_EVENT,cycle-log,0,wake up, sigio %i,sigio);更新 Nginx 内部缓存的各种时间值if(ngx_reap){ngx_reap0;ngx_log_debug0(NGX_LOG_DEBUG_EVENT,cycle-log,0,reap children);livengx_reap_children(cycle);}#1 ngx_reap 全局标志变量初始值为 0。 当 master 进程收到 SIGCHLD 信号时 信号处理函数会将 ngx_reap 设置为 1。 在 Unix/Linux 系统编程中 SIGCHLD 信号的核心作用是 当子进程的状态发生改变时 内核通过该信号异步通知父进程。 检查是否有子进程状态发生变化终止或暂停。 如果 ngx_reap 为真说明至少有一个子进程退出需要执行回收操作。#2 清除 ngx_reap 标志避免重复处理同一个信号 因为 SIGCHLD 可能会在短时间内多次触发但一次回收可以处理所有已退出的子进程。 确保每次 SIGCHLD 信号只触发一次回收逻辑#3 输出调试日志,记录回收动作触发#4 调用 ngx_reap_children 函数 遍历所有已注册的子进程 对每个已终止的子进程调用 waitpid 获取退出状态 返回值 live 1 表示还有至少一个活跃的子进程包括正在运行或重新生成的 0 表示所有子进程都已退出且没有重新生成。 live 用于后续判断 master 进程是否可以退出if(!live(ngx_terminate||ngx_quit)){ngx_master_process_exit(cycle);}判断 master 进程是否可以安全退出 !live 为真表示 没有活跃的子进程 (ngx_terminate || ngx_quit) 要其中任一标志为真即表示 master 已经收到了退出指令 调用 ngx_master_process_exit 函数 执行 master 进程的退出清理工作if(ngx_terminate){if(delay0){delay50;}if(sigio){sigio--;continue;}sigioccf-worker_processes2/* cache processes */;if(delay1000){ngx_signal_worker_processes(cycle,SIGKILL);}else{ngx_signal_worker_processes(cycle,ngx_signal_value(NGX_TERMINATE_SIGNAL));}continue;}全局标志 ngx_terminate 在信号处理函数中被设置为 1收到 SIGTERM 或 SIGINT。 一旦进入此分支表示 master 需要强制终止整个 Nginx 服务。if(ngx_quit){ngx_signal_worker_processes(cycle,ngx_signal_value(NGX_SHUTDOWN_SIGNAL));ngx_close_listening_sockets(cycle);continue;}全局标志 ngx_quit 在 SIGQUIT 信号处理函数中被设置为 1 含义用户要求 Nginx 优雅停止服务 即处理完当前所有正在进行的请求后再退出不中断现有连接ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); 函数作用向所有 worker 进程和 cache 管理进程发送指定的信号。 信号参数 NGX_SHUTDOWN_SIGNAL 通常映射为 SIGQUIT与 master 收到的信号相同。 子进程行为 Worker 进程收到 SIGQUIT 后会设置自己的 ngx_quit 标志进入优雅关闭流程 关闭监听套接字不再接受新连接。 处理完当前正在处理的请求后退出进程。 Cache 管理进程同样会收到 SIGQUIT执行相应的清理后退出。 意义 通知所有子进程开始优雅退出但不强制立即终止给它们时间完成已有工作。ngx_close_listening_sockets(cycle); 函数作用关闭 master 进程持有的所有监听套接字例如 80、443 端口。 Master 进程本身不处理连接 但它持有监听套接字的文件描述符在 cycle-listening 数组中。continue; 作用 跳过当前循环迭代中后续的其他信号处理分支例如 ngx_reconfigure、ngx_terminate 等 直接回到循环开头。 为什么需要 continue 因为已经进入优雅关闭流程 master 不应再响应其他信号如重新配置、重新打开日志等 否则可能干扰关闭过程。 回到循环开头后 会重新进入 sigsuspend 等待信号主要是 SIGCHLD等待子进程退出 后续当子进程逐个退出时ngx_reap 分支会回收它们并更新 live 变量。 当 live 0 且 ngx_quit 仍为真时master 会调用 ngx_master_process_exit 退出。if(ngx_reconfigure){ngx_reconfigure0;if(ngx_new_binary){ngx_start_worker_processes(cycle,ccf-worker_processes,NGX_PROCESS_RESPAWN);ngx_start_cache_manager_processes(cycle,0);ngx_noaccepting0;continue;}ngx_log_error(NGX_LOG_NOTICE,cycle-log,0,reconfiguring);cyclengx_init_cycle(cycle);if(cycleNULL){cycle(ngx_cycle_t*)ngx_cycle;continue;}ngx_cyclecycle;ccf(ngx_core_conf_t*)ngx_get_conf(cycle-conf_ctx,ngx_core_module);ngx_start_worker_processes(cycle,ccf-worker_processes,NGX_PROCESS_JUST_RESPAWN);ngx_start_cache_manager_processes(cycle,1);/* allow new processes to start */ngx_msleep(100);live1;ngx_signal_worker_processes(cycle,ngx_signal_value(NGX_SHUTDOWN_SIGNAL));}处理配置重载重启子进程if(ngx_restart){ngx_restart0;ngx_start_worker_processes(cycle,ccf-worker_processes,NGX_PROCESS_RESPAWN);ngx_start_cache_manager_processes(cycle,0);live1;}if(ngx_reopen){ngx_reopen0;ngx_log_error(NGX_LOG_NOTICE,cycle-log,0,reopening logs);ngx_reopen_files(cycle,ccf-user);ngx_signal_worker_processes(cycle,ngx_signal_value(NGX_REOPEN_SIGNAL));}重新打开所有日志文件if(ngx_change_binary){ngx_change_binary0;ngx_log_error(NGX_LOG_NOTICE,cycle-log,0,changing binary);ngx_new_binaryngx_exec_new_binary(cycle,ngx_argv);}用户请求 Nginx 平滑升级到新版本的可执行文件。if(ngx_noaccept){ngx_noaccept0;ngx_noaccepting1;ngx_signal_worker_processes(cycle,ngx_signal_value(NGX_SHUTDOWN_SIGNAL));}}}不再接受新连接通常用于平滑升级过程中让旧 worker 进程逐步退出。