当运行的nginx程序收到了 stop信号后是如何处理,使整个程序停下来的呢? 总结了下,主要有下面几个步骤:
一 master进程收到 stop 信号后,把 ngx_terminate 设置为1
二 master看到ngx_terminate 为1 ,通过channel向子进程发送 NGX_CMD_TERMINATE 命令
三 子进程收到命令后,把 ngx_terminate = 1
四 子进程看到 ngx_terminate 为1 , 直接调用ngx_worker_process_exit 结束进程
五 子进程结束的时候,会向master进程发送一个 SIGCHLD 信号
六 master进程收到sigchild信号后,把ngx_reap =1 并调用 ngx_process_get_status 得到退出的进程并设置进程的状态。
七 master看到ngx_reap 就调用 ngx_reap_children 回收子进程
八 都所有的子进程都回收完毕,master就调用 ngx_master_process_exit 退出。
下面对这些过程进行详细分析:
一 ngx_process.c 中的ngx_signal_handler 函数
//收到stop信号 case ngx_signal_value(NGX_TERMINATE_SIGNAL): case SIGINT: ngx_terminate = 1; action = ", exiting"; break;
二 在ngx_process_cycle.c 中古 ngx_master_process_cycle函数中
//收到stop信号 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; }
上面的delay等待时间,每次都翻倍,直到1000的时候,就强制杀进程。
sigio用户缓冲,防止频繁的向子进程发送信号。
三 ngx_process_cycle.c中的 ngx_channel_handler函数,主要如下:
//读取master进程的命令 n = ngx_read_channel(c->fd, &ch, sizeof(ngx_channel_t), ev->log); //设置value case NGX_CMD_TERMINATE: ngx_terminate = 1; break;
四 ngx_process_cycle.c 中的 ngx_worker_process_cycle函数中
//结束子进程 if (ngx_terminate) { ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting"); ngx_worker_process_exit(cycle); }
五 系统自动执行的
六 在ngx_process.c中的 ngx_signal_handler函数中
case SIGCHLD: ngx_reap = 1; //调用 if (signo == SIGCHLD) { ngx_process_get_status(); }
在ngx_process_get_status()函数中,很重要的一个操作就是把退出进程的exited 设置1
ngx_processes[i].exited = 1;
七 在ngx_process.c 中的ngx_master_process_cycle函数中
//回收子进程 if (ngx_reap) { ngx_reap = 0; ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children"); live = ngx_reap_children(cycle); }
在 ngx_reap_children中,
把 ngx_processes[i].exited 为1的进程进行回收。
存在退出中的进程 ,设置live为1
if (ngx_processes[i].exiting || !ngx_processes[i].detached) {
live = 1;
}
这样表示子还有子进程没有退出。
八 ngx_process_cycle.c中的ngx_master_process_cycle函数中
//收到结束的信号并且所有worker进程都死了 if (!live && (ngx_terminate || ngx_quit)) { ngx_master_process_exit(cycle); }
也就是需要所有worker进程都退出了,那么就退出master进程。
总结 : 整个过程通过channel 实现了父进程向子进程发送命令,子进程通过信号量向父进程报告自己的状态,整个过程非常清晰。