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 函数签名 void ngx_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_t size; ngx_int_t i; ngx_uint_t sigio; sigset_t set; struct itimerval itv; ngx_uint_t live; ngx_msec_t delay; 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 进程标题修改 size= sizeof ( master_process) ; for ( i= 0 ; i< ngx_argc; i++ ) { size+= ngx_strlen ( ngx_argv[ i] ) + 1 ; } 计算存储进程标题所需的内存大小 总长度 = 固定前缀长度 + ∑(每个启动参数的长度 + 1个空格分隔符) 该结果将直接用于下一步 ngx_pnalloc() 的内存分配请求。title= ngx_pnalloc ( cycle-> pool, size) ; if ( title== NULL ) { /* fatal */ exit ( 2 ) ; } 分配进程标题所需的内存p= ngx_cpymem ( title, master_process, sizeof ( master_process) - 1 ) ; for ( i= 0 ; i< ngx_argc; i++ ) { * p++ = ' ' ; p= ngx_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_binary= 0 ; delay= 0 ; sigio= 0 ; live= 1 ; 初始化 master 进程主循环的状态变量。 它们各自控制着 master 进程的终止流程、热升级状态以及子进程存活检测。作用:将“正在执行二进制热升级”的标志置为假。 逻辑:这是一个全局变量,当 master 接收到 SIGUSR2(NGX_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 会根据实际收割情况更新 live(0 表示所有子进程均已退出)。 意义:正确反映了当前系统中的子进程状态,使得退出逻辑能够准确判断何时安全退出。这些变量会在循环中被修改,控制 master 的行为。master 进程的主循环,永不主动退出for ( ; ; ) { if ( delay) { if ( ngx_sigalrm) { sigio= 0 ; delay*= 2 ; ngx_sigalrm= 0 ; } ngx_log_debug1 ( NGX_LOG_DEBUG_EVENT, cycle-> log, 0 , "termination cycle: %M" , delay) ; itv. it_interval. tv_sec= 0 ; itv. it_interval. tv_usec= 0 ; itv. it_value. tv_sec= delay/ 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_reap= 0 ; ngx_log_debug0 ( NGX_LOG_DEBUG_EVENT, cycle-> log, 0 , "reap children" ) ; live= ngx_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 ( delay== 0 ) { delay= 50 ; } if ( sigio) { sigio-- ; continue ; } sigio= ccf-> worker_processes+ 2 /* cache processes */ ; if ( delay> 1000 ) { 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_reconfigure= 0 ; if ( ngx_new_binary) { ngx_start_worker_processes ( cycle, ccf-> worker_processes, NGX_PROCESS_RESPAWN) ; ngx_start_cache_manager_processes ( cycle, 0 ) ; ngx_noaccepting= 0 ; continue ; } ngx_log_error ( NGX_LOG_NOTICE, cycle-> log, 0 , "reconfiguring" ) ; cycle= ngx_init_cycle ( cycle) ; if ( cycle== NULL ) { cycle= ( ngx_cycle_t * ) ngx_cycle; continue ; } ngx_cycle= cycle; 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 ) ; live= 1 ; ngx_signal_worker_processes ( cycle, ngx_signal_value ( NGX_SHUTDOWN_SIGNAL) ) ; } 处理配置重载重启子进程if ( ngx_restart) { ngx_restart= 0 ; ngx_start_worker_processes ( cycle, ccf-> worker_processes, NGX_PROCESS_RESPAWN) ; ngx_start_cache_manager_processes ( cycle, 0 ) ; live= 1 ; } if ( ngx_reopen) { ngx_reopen= 0 ; 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_binary= 0 ; ngx_log_error ( NGX_LOG_NOTICE, cycle-> log, 0 , "changing binary" ) ; ngx_new_binary= ngx_exec_new_binary ( cycle, ngx_argv) ; } 用户请求 Nginx 平滑升级到新版本的可执行文件。if ( ngx_noaccept) { ngx_noaccept= 0 ; ngx_noaccepting= 1 ; ngx_signal_worker_processes ( cycle, ngx_signal_value ( NGX_SHUTDOWN_SIGNAL) ) ; } } } 不再接受新连接,通常用于平滑升级过程中让旧 worker 进程逐步退出。