Nginx进程间通信–Signal篇
Nginx使用信号来管理master进程和worker进程。
信号Signal的概念
信号是软件中断。很多比较重要的应用程序都需要处理信号。信号提供了一种处理异步事件的方法。
当某个信号出现时,可以告诉内核按下列3种方式之一进行处理:
1、忽略信号。有两种信号不能被忽略,它们是SIGKILL和SIGSTOP,原因是它们向内核和超级用户提供了使进程终止或停止的可靠方法。另外,如果忽略某些由硬件异常产生的信号,则进程的运行行为是未定义的。
2、捕捉信号。通知内核在某种信号发生时,调用一个用户函数,可执行用户希望对这种事件进行的处理。自定义的信号处理函数必须是可重入的,即异步信号安全的。SIGKILL和SIGSTOP不能被捕捉。
3、执行系统默认动作。大多数信号的系统默认动作是终止该进程。
注:SIGKILL和SIGSTOP既不能被忽略,也不能被捕捉。
Nginx进程通信中Signal的使用
1、通过Signal发送控制信号
/home/nginx/sbin/nginx -h
nginx version: nginx/1.16.0
Usage: nginx [-?hvVtTq] [-s signal] [-c filename] [-p prefix] [-g directives]
Options:
-?,-h : this help
-v : show version and exit
-V : show version and configure options then exit
-t : test configuration and exit
-T : test configuration, dump it and exit
-q : suppress non-error messages during configuration testing
-s signal : send signal to a master process: stop, quit, reopen, reload
当我们向已经启动的nginx服务发送指令的时候,是通过发送signal实现的。
2、监控worker
Nginx的Master在监控到worker异常终止时,会自动拉起新的worker,其原理就是通过信号实现的。
Nginx的Master进程的自定义信号处理函数中,当受到SIGCHLD信号时,会将ngx_reap = 1,在Master的主进程循环中,会调用ngx_reap_children,在ngx_reap_children中会判断该worker进程满足条件(非exiting,非terminate,非quit状态),创建新的worker。
Nginx进程通信Signal的源码分析
Nginx定义ngx_signal_t,用于描述接收到信号时的行为。
typedef struct {
//信号值
int signo;
//信号名称
char *signame;
//nginx对应的名称,例如reload,reopen,stop等
char *name;
//nginx自定义信号处理函数
void (*handler)(int signo, siginfo_t *siginfo, void *ucontext);
} ngx_signal_t;
自定义的信号处理函数:
Master处理信号 | 全局变量值 |
---|---|
NGX_SHUTDOWN_SIGNAL | ngx_quit = 1 |
NGX_TERMINATE_SIGNAL | ngx_terminate = 1 |
NGX_NOACCEPT_SIGNAL | ngx_noaccept = 1 |
NGX_RECONFIGURE_SIGNAL | ngx_reconfigure = 1 |
NGX_REOPEN_SIGNAL | ngx_reopen = 1 |
NGX_CHANGEBIN_SIGNAL | ngx_change_binary = 1 |
SIGALRM | ngx_sigalrm = 1 |
SIGIO | ngx_sigio = 1 |
SIGCHLD | ngx_reap = 1 |
Worker处理信号 | 全局变量值 |
---|---|
NGX_NOACCEPT_SIGNAL | ngx_quit = 1 |
NGX_SHUTDOWN_SIGNAL | ngx_quit = 1 |
NGX_TERMINATE_SIGNAL | ngx_terminate = 1 |
SIGINT | ngx_terminate = 1 |
NGX_REOPEN_SIGNAL | ngx_reopen = 1 |
NGX_RECONFIGURE_SIGNAL | ignoring |
NGX_CHANGEBIN_SIGNAL | ignoring |
SIGIO | ignoring |
一、首先在main函数中调用ngx_init_signals,初始化所有需要自定义处理的信号。
ngx_int_t
ngx_init_signals(ngx_log_t *log)
{
......
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) {
......
}
}
......
return NGX_OK;
}
二、在master守护进程将所有信号添加到信号集里面。
void
ngx_master_process_cycle(ngx_cycle_t *cycle)
{
......
//清空信号集set,然后将需要自定义处理的信号添加到信号集中
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));
//屏蔽信号集set中的信号
if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"sigprocmask() failed");
}
......
sigemptyset(&set);
for ( ;; ) {
......
//重新设置信号集set,并挂起当前进程
sigsuspend(&set);
......
}
......
}
三、worker进程将所有信号添加到信号集里面。
static void
ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker)
{
sigemptyset(&set);
if (sigprocmask(SIG_SETMASK, &set, NULL) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"sigprocmask() failed");
}
}
四、master向worker发送信号:
static void
ngx_signal_worker_processes(ngx_cycle_t *cycle, int signo)
{
#if (NGX_BROKEN_SCM_RIGHTS)
ch.command = 0;
#else
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;
}
#endif
ch.fd = -1;
for (i = 0; i < ngx_last_process; i++) {
//detached=1,表示nginx平滑升级的进程,没有亲属关系的进程。
if (ngx_processes[i].detached || ngx_processes[i].pid == -1) {
continue;
}
//just_spawn,表示刚创建的子进程。
if (ngx_processes[i].just_spawn) {
ngx_processes[i].just_spawn = 0;
continue;
}
//正在退出的进程
if (ngx_processes[i].exiting
&& signo == ngx_signal_value(NGX_SHUTDOWN_SIGNAL))
{
continue;
}
if (ch.command) {
//如果能通过channel发送成功,则进入下一个循环
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;
}
}
ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,
"kill (%P, %d)", ngx_processes[i].pid, signo);
//直接通过kill函数向worker进程号发送信号
if (kill(ngx_processes[i].pid, signo) == -1) {
err = ngx_errno;
ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
"kill(%P, %d) failed", ngx_processes[i].pid, signo);
if (err == NGX_ESRCH) {
ngx_processes[i].exited = 1;
ngx_processes[i].exiting = 0;
ngx_reap = 1;
}
continue;
}
if (signo != ngx_signal_value(NGX_REOPEN_SIGNAL)) {
ngx_processes[i].exiting = 1;
}
}
......
}