一、说明
在前面一章,我们大致了解了nginx进程的启动,没有深入的去看进程到底做做了些什么事情,本章我们就看看master进程工作到底在做什么。二、函数分析
./src/os/unix/ngx_process_cycle.c>ngx_master_process_cycle(ngx_cycle_t *)ngx_new_binary = 0;
delay = 0;
sigio = 0;
live = 1;
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);
ngx_time_update();
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"wake up, sigio %i", sigio);
if (ngx_reap) {
ngx_reap = 0;
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children");
live = ngx_reap_children(cycle);
}
if (!live && (ngx_terminate || ngx_quit)) {
ngx_master_process_exit(cycle);
}
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;
}
if (ngx_quit) {
ngx_signal_worker_processes(cycle,
ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
ngx_close_listening_sockets(cycle);
continue;
}
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);
}
if (ngx_noaccept) {
ngx_noaccept = 0;
ngx_noaccepting = 1;
ngx_signal_worker_processes(cycle,
ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
}
}
老规矩,还是一段一段分析吧,不过这块代码看起来还是比较好懂的。
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");
}
}
setitimer是指经过指定时间之后,内核将发送SIGALRM信号给本进程。指定的时间就是itv。接收到指令之后,ngx_sigalrm就会变成1,当delay不为0的时候,会不断的翻倍。delay用来等待worker进程退出的时间,master进程接收到信号之后,会发信号给worker进程进行退出,worker进程退出需要时间,delay就是等待的时间。
sigsuspend(&set);
这个时候master进程会被挂起,当进程接受到新的信号之后,将会返回继续只是,同时将这个信号完成。上文提到过,处理与发起信号是通过ngx_init_signals执行ngx_signal_handler中的SIGALRM信号。ngx_signal_handler函数中就会将根据输入
设置下面的状态
ngx_time_update();
master进程处理信息的时候,更新缓存时间。
if (ngx_reap) {
ngx_reap = 0;
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children");
live = ngx_reap_children(cycle);
}
在上面提到过,进程挂起的时候,会监听对应的信号,这里当收到SIGCHLD信号的时候,进入这个逻辑。SIGCHLD信号是在子进程终止的时候向父进程发的信号。
ngx_reap_children函数会遍历ngx_process数组,检查每一个子进程的状态,对于异常退出的进程重新拉起。只有当子进程全部正常退出的时候,live才会是0,其他的时候返回的live为1。意思就是如果是异常退出,这里会将进程重新拉起,如果是有接收到退出命令的,那么就正常退出。
if (!live && (ngx_terminate || ngx_quit)) {
ngx_master_process_exit(cycle);
}
如果live为0就是子进程全部正常退出,并且根据输入的命令,如果是stop,就执行退出master进程的操作。
master退出的时候,会将pid的文件删掉,释放链接、释放cycle对象里面的内存空间、删除监听的事件等等,并且发出信号。
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超时时间,如果没有超时就执行
ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_TERMINATE_SIGNAL)); 发出TERM信号,正常退出worker进程 如果退出超时就执行,强制退出 ngx_signal_worker_processes(cycle, SIGKILL); 并且通过stop关闭的时候,就不关系是不是还有请求在处理,就直接关闭了,这样就会有quit命令。//TODO ngx_signal_worker_processes函数做了什么后面去填这个坑,至于什么时候。。随缘吧。。if (ngx_quit) {
ngx_signal_worker_processes(cycle,
ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
ngx_close_listening_sockets(cycle);
continue;
}
这个就是quit命令,相对上面就是没有业务之后,才会去关闭socket,比较优雅。
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));
}
这个对应的就是reload命令,重新读取配置文件。
ngx_new_binary这个标志位先不去看,直接看下面的。后续的动作就比较明显了,初始化ngx_cycle_t结构体,重新读取配置文件。启动新的worker进程跟Cache进程,然后等待启动完成,然后退去原来老的worker进程。 这里先不按照代码顺序讲,先说ngx_change_binary状态
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);
}
处理的信号是USR2。
nginx提供了平滑升级的能力。平滑启动之后,ngx_new_binary中保存的就是pid。所以上面的reload的时候ngx_new_binary判定是执行不到的。
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;
}
这个简单就是重启,没有对应的信号,就是在处理逻辑里面设置了ngx_restart就进行处理。
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));
}
就是对应的命令reopen,重新设置日志文件。
if (ngx_noaccept) {
ngx_noaccept = 0;
ngx_noaccepting = 1;
ngx_signal_worker_processes(cycle,
ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
}
所有的进程不在接收请求,进行从容关闭进程。