菜鳥學習Nginx之啟動流程(2)
上一篇介紹了啟動流程中關於初始化ngx_cycle_t。由於ngx_cycle_t是Nginx核心結構,Nginx整個架構均是圍繞它構建起來的。雖然用了一整篇文章介紹ngx_cycle_t,但是感覺還是有些內容沒有介紹清楚。初始化ngx_cycle_t有一部分程式碼沒有介紹,ngx_conf_parse,該函式解析配置檔案nginx.conf函式。此函式就是單純解析配置檔案,裡面程式碼比較枯燥乏味,理解上比較困難,所以沒有深入閱讀,也就在博文中沒有體現出來。
今天繼續介紹啟動流程後半部分內容--服務程序啟動。
一、master/worker模式
眾所周知,Nginx是單執行緒服務程序,它為了充分利用CPU多核特性(提升吞吐量、高併發),它採用使用多程序方式並且為了保證高可用性,又採用了一個管理程序(master程序)和多個服務程序(worker程序)模式。
1.1、職責
主要職責 | 備註 | |
master | 1、負責管理worker程序,例如:當worker程序異常退出,能夠及時排程起新的worker程序 2、接收外部訊號事件,例如:通過命令列發起平滑升級 |
外部程序只能通過訊號方式與master程序通訊,例如:命令列通過kill傳送訊息給master |
worker | 1、負責接收客戶端請求,例如:處理http請求 2、接收master程序指定,例如:master程序傳送Quit訊息,通知worker程序優雅退出 3、處理部分訊號 |
master與worker程序只能通過unix domain方式通訊。 |
二、啟動後臺程序
我們一般啟動程式,是通過登入終端,然後執行/usr/local/nginx/sbin/nginx。然後這樣啟動程序屬於前端程序,也就是說如果不加特殊處理,當終端關閉時nginx程序也會退出。那麼如何將前端程序變成後臺守護程序(精靈程序)?在main函式中有如下程式碼:
if (ngx_init_signals(cycle->log) != NGX_OK) {//初始化訊號 return 1; } /* fork出一個子程序,子程序為master 父程序(前端程序)退出*/ if (!ngx_inherited && ccf->daemon) { if (ngx_daemon(cycle->log) != NGX_OK) { return 1; } ngx_daemonized = 1; } if (ngx_inherited) { ngx_daemonized = 1; }
/**
* 生成守護程序
*/
ngx_int_t
ngx_daemon(ngx_log_t *log)
{
int fd;
switch (fork()) {//建立一個子程序
case -1:
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "fork() failed");
return NGX_ERROR;
case 0:
break;
default: //前端程序 直接退出
exit(0);
}
//設定各種資料
ngx_pid = ngx_getpid();
if (setsid() == -1) {//很關鍵 設定新會話id 這樣就與終端會話 脫離
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "setsid() failed");
return NGX_ERROR;
}
umask(0);
fd = open("/dev/null", O_RDWR);
if (fd == -1) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
"open(\"/dev/null\") failed");
return NGX_ERROR;
}
if (dup2(fd, STDIN_FILENO) == -1) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDIN) failed");
return NGX_ERROR;
}
if (dup2(fd, STDOUT_FILENO) == -1) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDOUT) failed");
return NGX_ERROR;
}
#if 0
if (dup2(fd, STDERR_FILENO) == -1) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDERR) failed");
return NGX_ERROR;
}
#endif
if (fd > STDERR_FILENO) {
if (close(fd) == -1) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "close() failed");
return NGX_ERROR;
}
}
return NGX_OK;
}
通過生成守護程序,這樣就能夠保證Nginx始終在後臺執行提供服務。
三、master程序
master程序,入口函式是ngx_master_process_cycle,該函式是在main函式呼叫,接下來重點分析一下該函式:
/**
* master程序主迴圈函式
*/
void ngx_master_process_cycle(ngx_cycle_t *cycle)
{
char *title;
u_char *p;
size_t size;
ngx_int_t i;
ngx_uint_t n, sigio;
sigset_t set;/* 訊號集 */
struct itimerval itv;
ngx_uint_t live;
ngx_msec_t delay;
ngx_listening_t *ls;
ngx_core_conf_t *ccf;
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));
/**
* 設定訊號遮蔽字
* 引數1: 操作型別
* SIG_BLOCK 將set訊號集與當前程序原有的訊號遮蔽字,進行或操作
* SIG_UNBLOCK 解除set指定的訊號
* SIG_SETMASK 將當前程序訊號遮蔽字設定為set訊號集。相當於重新賦值
* 引數2:
* 引數3: 該引數是輸出引數 返回當前程序設定的訊號遮蔽字
* 我的個人理解:
* 訊號的發生是百分之百的非同步,而且可能併發產生多個訊號。那麼如果應用
* 程序希望以阻塞方式對訊號進行處理,那麼就需要設定訊號遮蔽字。
*
* sigprocmask函式適用於單執行緒的程序
* pthread_sigmask函式適用於多執行緒的程序
*/
if (sigprocmask(SIG_BLOCK, &set, NULL) == -1)
{
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"sigprocmask() failed");
}
sigemptyset(&set);
//計算命令列引數,用於重新設定程序名稱
size = sizeof(master_process);
for (i = 0; i < ngx_argc; i++)
{
size += ngx_strlen(ngx_argv[i]) + 1;
}
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);
}
ngx_setproctitle(title);//設定master程序名稱
ccf = (ngx_core_conf_t *)ngx_get_conf(cycle->conf_ctx, ngx_core_module);
//啟動worker程序,啟動成功之後就返回
ngx_start_worker_processes(cycle, ccf->worker_processes,
NGX_PROCESS_RESPAWN);
ngx_start_cache_manager_processes(cycle, 0);//啟動監控程序 預設不啟動
ngx_new_binary = 0;
delay = 0;
sigio = 0;
live = 1; //表示是否活躍
這段程式碼邏輯並不是很複雜,對於訊號這部分處理,我並不是很熟悉,我深入瞭解了一下,並將其寫到註釋中。如果還有不清楚的,建議看一下《Unix環境程式設計》。 上面程式碼中ngx_start_worker_processes函式用於建立worker程序,具體內容在下一面一小節中會詳細介紹。接下來,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才返回,執行後續程式碼
*/
sigsuspend(&set);
ngx_time_update();
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"wake up, sigio %i", sigio);
if (ngx_reap)
{//當子程序異常退出時,會接收到SIGCHLD訊號,因此會在呼叫起來一個子程序
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)
{//接收到TERM訊號 理應立即關閉 但是Nginx採用延遲關閉方式
if (delay == 0)
{
delay = 50; //50ms
}
if (sigio)
{
sigio--;
continue;
}
sigio = ccf->worker_processes + 2 /* cache processes */;
if (delay > 1000)
{//如果延遲大於1000ms 則暴力關閉程序
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));
ls = cycle->listening.elts;
for (n = 0; n < cycle->listening.nelts; n++)
{
if (ngx_close_socket(ls[n].fd) == -1)
{
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,
ngx_close_socket_n " %V failed",
&ls[n].addr_text);
}
}
cycle->listening.nelts = 0;
continue;
}
/**
* 當master程序接收到HUP訊號,用於重新載入配置。例如:配置檔案變化,需要
* 更新配置
*/
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));
}
/**
* 表示重啟worker程序,進入此分支的前提是master程序接收到SIGCHLD訊號,即
* worker程序異常退出
*/
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;
}
/**
* 當master程序接收到USR1訊號,表明需要重新開啟日誌檔案
*/
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));
}
/**
* 當master程序接收到USR2訊號,表明進行平滑升級
*/
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);
}
/**
* master程序收到WINCH訊號(通過kill傳送)後,會通過channel傳送QUIT訊息
* 給worker程序,當worker程序接收到QUIT訊息就會優雅退出
*/
if (ngx_noaccept)
{
ngx_noaccept = 0;
ngx_noaccepting = 1;
ngx_signal_worker_processes(cycle,
ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
}
master程序處理邏輯並不複雜,畢竟master程序是比較清閒的。master程序主要處理各種訊號事件,master也沒有事件驅動(epoll),並且worker程序也不會主動發訊息給master。
四、worker程序
接下來看一下,worker程序。worker程序處理邏輯相比master程序就複雜了,但是這裡並不想介紹特別深入,只做到點睛之筆就行。因為後面還會專題進行詳細介紹。
/**
* 建立worker程序
* @param cycle 核心結構體
* @param n worker程序數量
* @param type 建立worker程序方式
*/
static void
ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type)
{
ngx_int_t i;
ngx_channel_t ch;
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start worker processes");
ngx_memzero(&ch, sizeof(ngx_channel_t));
ch.command = NGX_CMD_OPEN_CHANNEL; //傳送第一個訊息
for (i = 0; i < n; i++)
{
//啟動多個worker程序 回撥函式為ngx_worker_process_cycle
ngx_spawn_process(cycle, ngx_worker_process_cycle,
(void *)(intptr_t)i, "worker process", type);
ch.pid = ngx_processes[ngx_process_slot].pid; /* 子程序id */
ch.slot = ngx_process_slot; /* 子程序在ngx_processes陣列中索引 */
ch.fd = ngx_processes[ngx_process_slot].channel[0]; /* 父程序socketpair fd */
ngx_pass_open_channel(cycle, &ch);//通過unix domain傳送第一個訊息給worker程序
}
}
程式碼比較簡單,在看ngx_spawn_process函式之前,先來看一下流程圖,如下:
通過流程圖可知,該函式處理邏輯比較簡單,下面是具體內容:
/**
* 生成子程序
* @param cycle 核心結構體
* @param proc 子程序程序函式
* @param data 子程序程序函式,入參
* @param name 子程序程序名稱
* @param respawn 生產子程序方式
*/
ngx_pid_t
ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data,
char *name, ngx_int_t respawn)
{
u_long on;
ngx_pid_t pid; /* 子程序id */
ngx_int_t s; /* 子程序在陣列ngx_processes中索引 */
/* 確定子程序在陣列ngx_processes中索引 */
if (respawn >= 0) {
s = respawn;
} else {
for (s = 0; s < ngx_last_process; s++) {
if (ngx_processes[s].pid == -1) {
break;
}
}
if (s == NGX_MAX_PROCESSES) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"no more than %d processes can be spawned",
NGX_MAX_PROCESSES);
return NGX_INVALID_PID;
}
}
if (respawn != NGX_PROCESS_DETACHED) {
/* Solaris 9 still has no AF_LOCAL */
/* 建立socketpair 並且設定socket 選項 */
if (socketpair(AF_UNIX, SOCK_STREAM, 0, ngx_processes[s].channel) == -1)
{
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"socketpair() failed while spawning \"%s\"", name);
return NGX_INVALID_PID;
}
ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,
"channel %d:%d",
ngx_processes[s].channel[0],
ngx_processes[s].channel[1]);
if (ngx_nonblocking(ngx_processes[s].channel[0]) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
ngx_nonblocking_n " failed while spawning \"%s\"",
name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
}
if (ngx_nonblocking(ngx_processes[s].channel[1]) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
ngx_nonblocking_n " failed while spawning \"%s\"",
name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
}
on = 1;
if (ioctl(ngx_processes[s].channel[0], FIOASYNC, &on) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"ioctl(FIOASYNC) failed while spawning \"%s\"", name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
}
if (fcntl(ngx_processes[s].channel[0], F_SETOWN, ngx_pid) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"fcntl(F_SETOWN) failed while spawning \"%s\"", name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
}
/* FD_CLOEXEC標誌 表示當執行exec家族函式時自動關閉當前檔案控制代碼 */
if (fcntl(ngx_processes[s].channel[0], F_SETFD, FD_CLOEXEC) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"fcntl(FD_CLOEXEC) failed while spawning \"%s\"",
name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
}
if (fcntl(ngx_processes[s].channel[1], F_SETFD, FD_CLOEXEC) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"fcntl(FD_CLOEXEC) failed while spawning \"%s\"",
name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
}
ngx_channel = ngx_processes[s].channel[1];
} else {
ngx_processes[s].channel[0] = -1;
ngx_processes[s].channel[1] = -1;
}
ngx_process_slot = s;
pid = fork(); //建立子程序
switch (pid) {
case -1:
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"fork() failed while spawning \"%s\"", name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
case 0:
ngx_pid = ngx_getpid();//子程序 fork返回值0
proc(cycle, data); //子程序一直迴圈,不會結束。當結束時子程序也就是exit
break;
default://父程序 fork返回值為非0 是子程序 程序id
break;
}
/**
* 以下程式碼是父程序執行 子程序永遠不會執行到這裡. 因此子程序再退出時直接
* 呼叫exit 沒有機會執行下列程式碼
*/
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start %s %P", name, pid);
/* 設定子程序資訊 */
ngx_processes[s].pid = pid;
ngx_processes[s].exited = 0;
if (respawn >= 0) {
return pid;
}
ngx_processes[s].proc = proc;
ngx_processes[s].data = data;
ngx_processes[s].name = name;
ngx_processes[s].exiting = 0;
switch (respawn) {
case NGX_PROCESS_NORESPAWN:
ngx_processes[s].respawn = 0;
ngx_processes[s].just_spawn = 0;
ngx_processes[s].detached = 0;
break;
case NGX_PROCESS_JUST_SPAWN:
ngx_processes[s].respawn = 0;
ngx_processes[s].just_spawn = 1;
ngx_processes[s].detached = 0;
break;
case NGX_PROCESS_RESPAWN:
ngx_processes[s].respawn = 1;
ngx_processes[s].just_spawn = 0;
ngx_processes[s].detached = 0;
break;
case NGX_PROCESS_JUST_RESPAWN:
ngx_processes[s].respawn = 1;
ngx_processes[s].just_spawn = 1;
ngx_processes[s].detached = 0;
break;
case NGX_PROCESS_DETACHED:
ngx_processes[s].respawn = 0;
ngx_processes[s].just_spawn = 0;
ngx_processes[s].detached = 1;
break;
}
if (s == ngx_last_process) {
ngx_last_process++;
}
return pid;
}
四、訊號
訊號註冊是在main函式中呼叫,具體註冊內容比較簡單,如下:
/**
* 訊號註冊
*/
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));
sa.sa_handler = sig->handler;/* 訊號處理函式 */
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;
}
當有訊號事件發生,程序會發生中斷,然後呼叫訊號處理函式,如下為訊號中斷處理函式:
static void
ngx_signal_handler(int signo)
{
char *action;
ngx_int_t ignore;
ngx_err_t err;
ngx_signal_t *sig;
ignore = 0;
err = ngx_errno;
for (sig = signals; sig->signo != 0; sig++) {
if (sig->signo == signo) {
break;
}
}
ngx_time_sigsafe_update();
action = "";
switch (ngx_process) {
/* master程序處理訊號 */
case NGX_PROCESS_MASTER:
case NGX_PROCESS_SINGLE:
switch (signo) {
case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
ngx_quit = 1;
action = ", shutting down";
break;
case ngx_signal_value(NGX_TERMINATE_SIGNAL):
case SIGINT:
ngx_terminate = 1;
action = ", exiting";
break;
case ngx_signal_value(NGX_NOACCEPT_SIGNAL):
if (ngx_daemonized) {
ngx_noaccept = 1;
action = ", stop accepting connections";
}
break;
case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):
ngx_reconfigure = 1;
action = ", reconfiguring";
break;
case ngx_signal_value(NGX_REOPEN_SIGNAL):
ngx_reopen = 1;
action = ", reopening logs";
break;
/* 用於平滑升級 USR2 */
case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):
if (getppid() > 1 || ngx_new_binary > 0) {
/*
* Ignore the signal in the new binary if its parent is
* not the init process, i.e. the old binary's process
* is still running. Or ignore the signal in the old binary's
* process if the new binary's process is already running.
*/
action = ", ignoring";
ignore = 1;
break;
}
ngx_change_binary = 1;
action = ", changing binary";
break;
case SIGALRM:
ngx_sigalrm = 1;
break;
case SIGIO:
ngx_sigio = 1;
break;
case SIGCHLD:
ngx_reap = 1;
break;
}
break;
/* worker程序處理相關訊號 */
case NGX_PROCESS_WORKER:
case NGX_PROCESS_HELPER:
switch (signo) {
case ngx_signal_value(NGX_NOACCEPT_SIGNAL):
if (!ngx_daemonized) {
break;
}
ngx_debug_quit = 1;
/* fall through */
case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
ngx_quit = 1;
action = ", shutting down";
break;
case ngx_signal_value(NGX_TERMINATE_SIGNAL):
case SIGINT:
ngx_terminate = 1;
action = ", exiting";
break;
case ngx_signal_value(NGX_REOPEN_SIGNAL):
ngx_reopen = 1;
action = ", reopening logs";
break;
/* worker程序忽略HUP USR2訊號 */
case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):
case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):
case SIGIO:
action = ", ignoring";
break;
}
break;
}
ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
"signal %d (%s) received%s", signo, sig->signame, action);
if (ignore) {
ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, 0,
"the changing binary signal is ignored: "
"you should shutdown or terminate "
"before either old or new binary's process");
}
if (signo == SIGCHLD) {/* 表示worker程序有退出 */
ngx_process_get_status();
}
ngx_set_errno(err);
}
通過中斷處理函式可知,有兩點不同:
1、master程序處理訊號種類要比worker程序多,worker程序會忽略訊號HUP,USR2。
2、給master傳送訊號一般都是通過kill或者nginx -s方式,然而給worker程序一般是通過master程序直接呼叫kill函式傳送訊號。
五、總結
本篇介紹Nginx啟動流程中master/worker模式,從程式碼中可知,master程序處理邏輯並不是很複雜。複雜的地方是在於worker程序。再下一篇將會詳細介紹worker程序主函式ngx_worker_process_cycle。