nginx在收到stop信号后的处理
Posted ncist-m
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了nginx在收到stop信号后的处理相关的知识,希望对你有一定的参考价值。
1 启动时指定信号处理函数
在nginx启动的时候就会指定信号的处理函数:
ngx_int_t ngx_init_signals(ngx_log_t *log) { ngx_signal_t *sig; struct sigaction sa; for (sig = signals; sig->signo != 0; sig++) { ngx_memzero(&sa, sizeof(struct sigaction)); if (sig->handler) { sa.sa_sigaction = sig->handler; sa.sa_flags = SA_SIGINFO; } else { sa.sa_handler = SIG_IGN; } sigemptyset(&sa.sa_mask); if (sigaction(sig->signo, &sa, NULL) == -1) { #if (NGX_VALGRIND) ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "sigaction(%s) failed, ignored", sig->signame); #else ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "sigaction(%s) failed", sig->signame); return NGX_ERROR; #endif } } return NGX_OK; }
其中,signals结构体如下:
1 ngx_signal_t signals[] = {
2 { ngx_signal_value(NGX_RECONFIGURE_SIGNAL),
3 "SIG" ngx_value(NGX_RECONFIGURE_SIGNAL),
4 "reload",
5 ngx_signal_handler },
6
7 { ngx_signal_value(NGX_REOPEN_SIGNAL),
8 "SIG" ngx_value(NGX_REOPEN_SIGNAL),
9 "reopen",
10 ngx_signal_handler },
11
12 { ngx_signal_value(NGX_NOACCEPT_SIGNAL),
13 "SIG" ngx_value(NGX_NOACCEPT_SIGNAL),
14 "",
15 ngx_signal_handler },
16
17 { ngx_signal_value(NGX_TERMINATE_SIGNAL),
18 "SIG" ngx_value(NGX_TERMINATE_SIGNAL),
19 "stop",
20 ngx_signal_handler },
21
22 { ngx_signal_value(NGX_SHUTDOWN_SIGNAL),
23 "SIG" ngx_value(NGX_SHUTDOWN_SIGNAL),
24 "quit",
25 ngx_signal_handler },
26
27 { ngx_signal_value(NGX_CHANGEBIN_SIGNAL),
28 "SIG" ngx_value(NGX_CHANGEBIN_SIGNAL),
29 "",
30 ngx_signal_handler },
31
32 { SIGALRM, "SIGALRM", "", ngx_signal_handler },
33
34 { SIGINT, "SIGINT", "", ngx_signal_handler },
35
36 { SIGIO, "SIGIO", "", ngx_signal_handler },
37
38 { SIGCHLD, "SIGCHLD", "", ngx_signal_handler },
39
40 { SIGSYS, "SIGSYS, SIG_IGN", "", NULL },
41
42 { SIGPIPE, "SIGPIPE, SIG_IGN", "", NULL },
43
44 { 0, NULL, "", NULL }
45 }
2 执行nginx -s stop后的处理过程
执行nginx -s stop会启动一个新的进程,这个进程的任务主要是打开之前的pid文件,然后向之前的master进程发送一个NGX_TERMINATE_SIGNAL 信号(这个只是个代号,真正的信号的名字是由字符串拼接的,跟下就能找到)。然后退出
在ngx_get_options中判断参数类型,把静态char指针ngx_signal赋值stop
在ngx_signal_process函数中获取master进程号,并调用ngx_os_signal_process
在ngx_os_signal_process函数中根据信号类型向master发送信号:
1 for (sig = signals; sig->signo != 0; sig++) { 2 if (ngx_strcmp(name, sig->name) == 0) { 3 if (kill(pid, sig->signo) != -1) { 4 return 0; 5 } 6 7 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, 8 "kill(%P, %d) failed", pid, sig->signo); 9 } 10 }
3 master收到信号后的处理
不难看出在nginx启动时,就指定了信号的处理函数,而收到stop信号的处理函数为ngx_signal_handler,在这个函数中设置了全局变量ngx_ternimate = 1;
1 static void 2 ngx_signal_handler(int signo, siginfo_t *siginfo, void *ucontext) 3 { 4 char *action; 5 ngx_int_t ignore; 6 ngx_err_t err; 7 ngx_signal_t *sig; 8 9 ignore = 0; 10 11 err = ngx_errno; 12 13 for (sig = signals; sig->signo != 0; sig++) { 14 if (sig->signo == signo) { 15 break; 16 } 17 } 18 19 ngx_time_sigsafe_update(); 20 21 action = ""; 22 23 switch (ngx_process) { 24 25 case NGX_PROCESS_MASTER: 26 case NGX_PROCESS_SINGLE: 27 switch (signo) { 28 29 case ngx_signal_value(NGX_SHUTDOWN_SIGNAL): 30 ngx_quit = 1; 31 action = ", shutting down"; 32 break; 33 34 case ngx_signal_value(NGX_TERMINATE_SIGNAL): 35 case SIGINT: 36 ngx_terminate = 1; 37 action = ", exiting"; 38 break; 39 40 case ngx_signal_value(NGX_NOACCEPT_SIGNAL): 41 if (ngx_daemonized) { 42 ngx_noaccept = 1; 43 action = ", stop accepting connections"; 44 } 45 break; 46 47 case ngx_signal_value(NGX_RECONFIGURE_SIGNAL): 48 ngx_reconfigure = 1; 49 action = ", reconfiguring"; 50 break; 51 52 case ngx_signal_value(NGX_REOPEN_SIGNAL): 53 ngx_reopen = 1; 54 action = ", reopening logs"; 55 break; 56 57 case ngx_signal_value(NGX_CHANGEBIN_SIGNAL): 58 if (ngx_getppid() == ngx_parent || ngx_new_binary > 0) { 59 60 /* 61 * Ignore the signal in the new binary if its parent is 62 * not changed, i.e. the old binary‘s process is still 63 * running. Or ignore the signal in the old binary‘s 64 * process if the new binary‘s process is already running. 65 */ 66 67 action = ", ignoring"; 68 ignore = 1; 69 break; 70 } 71 72 ngx_change_binary = 1; 73 action = ", changing binary"; 74 break; 75 76 case SIGALRM: 77 ngx_sigalrm = 1; 78 break; 79 80 case SIGIO: 81 ngx_sigio = 1; 82 break; 83 84 case SIGCHLD: 85 ngx_reap = 1; 86 break; 87 } 88 89 break; 90 91 case NGX_PROCESS_WORKER: 92 case NGX_PROCESS_HELPER: 93 switch (signo) { 94 95 case ngx_signal_value(NGX_NOACCEPT_SIGNAL): 96 if (!ngx_daemonized) { 97 break; 98 } 99 ngx_debug_quit = 1; 100 /* fall through */ 101 case ngx_signal_value(NGX_SHUTDOWN_SIGNAL): 102 ngx_quit = 1; 103 action = ", shutting down"; 104 break; 105 106 case ngx_signal_value(NGX_TERMINATE_SIGNAL): 107 case SIGINT: 108 ngx_terminate = 1; 109 action = ", exiting"; 110 break; 111 112 case ngx_signal_value(NGX_REOPEN_SIGNAL): 113 ngx_reopen = 1; 114 action = ", reopening logs"; 115 break; 116 117 case ngx_signal_value(NGX_RECONFIGURE_SIGNAL): 118 case ngx_signal_value(NGX_CHANGEBIN_SIGNAL): 119 case SIGIO: 120 action = ", ignoring"; 121 break; 122 } 123 124 break; 125 } 126 127 if (siginfo && siginfo->si_pid) { 128 ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, 129 "signal %d (%s) received from %P%s", 130 signo, sig->signame, siginfo->si_pid, action); 131 132 } else { 133 ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, 134 "signal %d (%s) received%s", 135 signo, sig->signame, action); 136 } 137 138 if (ignore) { 139 ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, 0, 140 "the changing binary signal is ignored: " 141 "you should shutdown or terminate " 142 "before either old or new binary‘s process"); 143 } 144 145 if (signo == SIGCHLD) { 146 ngx_process_get_status(); 147 } 148 149 ngx_set_errno(err); 150 }
设置了ngx_terimate之后直接影响master主循环体中这一段:
1 void 2 ngx_master_process_cycle(ngx_cycle_t *cycle) 3 { 4 ... 5 for ( ;; ) { 6 if (ngx_reap) { 7 ngx_reap = 0; 8 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children"); 9 10 live = ngx_reap_children(cycle); 11 } 12 13 if (!live && (ngx_terminate || ngx_quit)) { 14 ngx_master_process_exit(cycle); 15 } 16 17 if (ngx_terminate) { 18 if (delay == 0) { 19 delay = 50; 20 } 21 22 if (sigio) { 23 sigio--; 24 continue; 25 } 26 27 sigio = ccf->worker_processes + 2 /* cache processes */; 28 29 if (delay > 1000) { 30 ngx_signal_worker_processes(cycle, SIGKILL); 31 } else { 32 ngx_signal_worker_processes(cycle, 33 ngx_signal_value(NGX_TERMINATE_SIGNAL)); 34 } 35 36 continue; 37 } 38 }
在ngx_signal_worker_processes函数中,首先通过ipc通信write channel 向所有worker进程发送terminal 信号。发送失败的情况下,直接kill。
work进程收到信号并处理
work进程启动时,ngx_worker_process_cycle-->ngx_worker_process_init-->
1 if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT, 2 ngx_channel_handler) 3 == NGX_ERROR) 4 { 5 /* fatal */ 6 exit(2); 7 }
在ngx_channel_handler中设置了ngx_terminate为1;
而在work进程主循环中, 检测到该标志位1,则启动ngx_worker_process_exit函数。
1 for(;;) { 2 if (ngx_terminate) { 3 ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting"); 4 ngx_worker_process_exit(cycle); 5 } 6 }
现象
执行一段时间后,stop后,进程残留。成为僵尸进程。
疑问
1、执行./nginx -s stop之后,master进程收到好几个SIGALERT信号,但是从代码上来看,其实只发送了一次信号。但是为什么master进程会收到N次信号后退出。
2、worker进程在销毁的时候在thread 模块中没出来,但是在master进程收到N次信号后,worker进程出来了,然后master进程接着退出。为什么worker进程的thread 模块的exit process没出来也退出了worker进程。
以上是关于nginx在收到stop信号后的处理的主要内容,如果未能解决你的问题,请参考以下文章