命令
nginx -s quit 或 kill -s SIGQUIT <nginx master pid> 可以優(yōu)雅的關閉服務。它們都是通過發(fā)送信號實現(xiàn)的。具體見——向master進程發(fā)送信號。
具體過程
由 向master進程發(fā)送信號 一文可知,捕捉到信號并處理后,master進程被喚醒,它將檢測被信號處理函數(shù)改寫過的全局變量的值,進而決定下一步的執(zhí)行。
當在終端執(zhí)行 nginx -s quit 或 kill -s SIGQUIT <nginx master pid>命令后,全局變量ngx_quit被置位為1;
//ngx_signal_handler函數(shù)代碼片段
case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
ngx_quit = 1;
action = ", shutting down";
break;
master進程循環(huán)內(nèi)部,與quit相關代碼如下所示。
首先,調用ngx_signal_worker_processed函數(shù),第二個參數(shù)ngx_signal_value(NGX_SHUTDOWN_SIGNAL)實際上就是SIGQUIT信號,該函數(shù)將通知所有子進程終止。
其次,關閉所有監(jiān)聽socket,并復位cycle指針指向的配置項結構體的成員listening,該成員與監(jiān)聽socket有關。
if (ngx_quit) {
ngx_signal_worker_processes(cycle,ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
ls = cycle->listening.elts;
for (n = 0; n < cycle->listening.nelts; n++) {
if (ngx_close_socket(ls[n].fd) == -1) {
ngx_log_error(...)
}
}
cycle->listening.nelts = 0;
continue;
}
下面看ngx_signal_worker_processed函數(shù)
//片段1
static void
ngx_signal_worker_processes(ngx_cycle_t *cycle, int signo)
{
......
ngx_channel_t ch;
switch (signo) {
case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
ch.command = NGX_CMD_QUIT;
break;
case ngx_signal_value(NGX_TERMINATE_SIGNAL):
ch.command = NGX_CMD_TERMINATE;
break;
case ngx_signal_value(NGX_REOPEN_SIGNAL):
ch.command = NGX_CMD_REOPEN;
break;
default:
ch.command = 0;
}
......
}
ngx_signal_worker_processed函數(shù)中有一個重要的結構體ngx_channel_t,具體如下:
typedef struct {
ngx_uint_t command;//傳遞的TCP消息中的命令
ngx_pid_t pid; //一般是發(fā)送命令方的進程id
ngx_int_t slot;//表示發(fā)送命令方在ngx_processes進程數(shù)組間的序號
ngx_fd_t fd;//通信的套接字句柄
} ngx_channel_t;
通過該結構體和與其相關的三個函數(shù)實現(xiàn)master進程與worker進程的通信,具體見 master與worker的通信機制 一文。先簡單介紹下這三個函數(shù):
ngx_open_channel——建立通信通道
ngx_write_channel——向另一進程發(fā)送消息
ngx_read_channel——讀取所獲得的消息
當判斷信號為 ngx_signal_value(NGX_SHUTDOWN_SIGNAL),即SIGQUIT后,令ch.command = NGX_CMD_QUIT,調用ngx_write_channel函數(shù)將給命令發(fā)送給子進程。
//通過CHANNEL發(fā)送命令給待終止的子進程,子進程調用ngx_channel_handler 處理
//由于是非阻塞socket,函數(shù)返回前,命令將被傳遞給子進程
if (ch.command) {
if (ngx_write_channel(ngx_processes[i].channel[0],
&ch, sizeof(ngx_channel_t), cycle->log)
== NGX_OK)
{
if (signo != ngx_signal_value(NGX_REOPEN_SIGNAL)) {
ngx_processes[i].exiting = 1;
}
continue;
}
}