这篇文章讲解nginx的信号机制,因为是信号,所以其他方面的没有深究,放在后面分享出来,欢迎有兴趣的朋友留言交流
nginx运行的进程模式为master-worker的话,那master和worker进程间是通过信号来通信,关于信号相关的知识可以参考下面的链接Linux信号(signal) 机制分析
下面讲解代码执行流程都是以quit命令来执行的
一、信号的安装
nginx启动的过程中,master进程会安装自定义的信号处理机制,定义了哪些信号需要自己安装处理函数,nginx.c中的main函数中,有个ngx_init_signals函数就是用来安装自定义信号的,安装之后,后面fork出来的worker进程也会继承安装的自定义信号处理机制,因为fork出来的worker进程会复制一份和父进程一模一样的地址空间,包括所有资源,我们看下代码
ngx_int_t
ngx_init_signals(ngx_log_t *log)
{
ngx_signal_t *sig;
struct sigaction sa;
//signals已经是初始化过的全局变量,存储的就是各个信号的回调函数相关信息
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这个全局变量的内容,这些信号就是对应nginx命令行的一些处理,例如nginx -s stop|quit|reload....
ngx_signal_t signals[] = {
{ ngx_signal_value(NGX_RECONFIGURE_SIGNAL),
"SIG" ngx_value(NGX_RECONFIGURE_SIGNAL),
"reload",
ngx_signal_handler },
{ ngx_signal_value(NGX_REOPEN_SIGNAL),
"SIG" ngx_value(NGX_REOPEN_SIGNAL),
"reopen",
ngx_signal_handler },
{ ngx_signal_value(NGX_NOACCEPT_SIGNAL),
"SIG" ngx_value(NGX_NOACCEPT_SIGNAL),
"",
ngx_signal_handler },
{ ngx_signal_value(NGX_TERMINATE_SIGNAL),
"SIG" ngx_value(NGX_TERMINATE_SIGNAL),
"stop",
ngx_signal_handler },
{ ngx_signal_value(NGX_SHUTDOWN_SIGNAL),
"SIG" ngx_value(NGX_SHUTDOWN_SIGNAL),
"quit",
ngx_signal_handler },
{ ngx_signal_value(NGX_CHANGEBIN_SIGNAL),
"SIG" ngx_value(NGX_CHANGEBIN_SIGNAL),
"",
ngx_signal_handler },
{ SIGALRM, "SIGALRM", "", ngx_signal_handler },
{ SIGINT, "SIGINT", "", ngx_signal_handler },
{ SIGIO, "SIGIO", "", ngx_signal_handler },
{ SIGCHLD, "SIGCHLD", "", ngx_signal_handler },
{ SIGSYS, "SIGSYS, SIG_IGN", "", NULL },
{ SIGPIPE, "SIGPIPE, SIG_IGN", "", NULL },
{ 0, NULL, "", NULL }
};
安装完nginx要处理的信号之后,因为我们这篇讲的是nginx信号的机制,所以我们直接看ngx_master_process_cycle
二、 master进程阻塞等待信号
1、首先初始化一个信号集并且置空,sigemptyset(&set);
2、然后将需要处理的信号添加到信号集中,sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL));
3、先暂时用sigprocmask(SIG_BLOCK, &set, NULL),将上面的信号集阻塞,此刻如果有信号产生,内核不会把信号传递给进程,就是所谓的阻塞
4、然后信号集再置为空,sigemptyset(&set);
5、在master无限循环中,用sigsuspend(&set);,将之前阻塞的信号从掩码信号集中删除,然后挂起等待信号
就这样master进程就会阻塞在sigsuspend的调用中,直到有信号产生。
接下来我们看下如果有信号产生,接下来会怎么执行
当有信号产生的时候,nginx会捕获信号然后调用定义的信号处理函数(就是之前安装的信号处理器),信号处理函数主要是设置一些信号标识,然后sigsuspend调用返回,然后继续执行下面的代码,可以看源码中下面的
if (ngx_terminate) if (ngx_quit) 一些信号标识的判断,就是根据信号处理函数处理的信号标识进行不同的操作。
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;
//将参数set信号集初始化并清空
sigemptyset(&set);
//将参数signum 代表的信号加入至参数set 信号集里
//SIGCHLD子进程退出发出的信号
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(NG