上一篇介绍了启动流程中关于初始化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: 该参数是输出参数 返回当前进程设置的信号屏蔽字
* 个人理解:
* 信号忽略与信号屏蔽,两种不同行为:
* 信号忽略: 指的是操作系统将信号发给进程,进程对信号的处理默认是行为是忽略
* 信号屏蔽: 产生某个信号后,操作系统暂时不发给进程(有操作系统阻塞),等进程取消屏蔽后在发给
* 进程。
* 对于nginx来说,在主进程fork子进程之前要把所有信号调用sigprocmask阻塞住,
* 等待fork成功后再将阻塞信号清除
*
*
* 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。