leevis.com
leevis.com copied to clipboard
nginx中的信号处理
概述
通过向nginx的master进程发送信号来管理nginx。例如:向nginx进程发送sighup信号重新加载配置。操作:kill -1 pid 或者nginx -s reload。
知识回顾
linux信号是进程间通信的一种方式,信号是一种软中断,实际上也属于进程控制的一部分。 软中断信号signal用来通知进程发生了异步事件,进程之间通过kill系统调用发送软中断,内核也会因内部事件而给进程发送信号,通知发生了某个事件。信号仅通知发生了什么事件,并不给进程传递数据。
收到信号的进程可以有3种方式来处理,1: 进程通过signal等函数指定对应的处理函数。2: 忽略信号。3: 系统默认的处理方式。
实际执行信号的处理动作称为信号递达(delivery),信号从产生到递达之间的状态称为信号未决(pending)。进程可以选择阻塞信号,被阻塞的信号产生时保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。sigsuspend函数可以临时解除信号阻塞。
下面是信号相关的函数:
注册信号回调函数
// sig 指定信号编号
// func 处理回调函数,SIG_IGN表示忽略信号。SIG_DFL表示采用系统默认处理方式。
void (*signal(int sig, void (*func)(int)))(int);
struct sigaction
{
// 同signal函数的回调
void (*sa_handler) (int);
// 新的信号处理函数
void (*sa_sigaction)(int, siginfo_t *, void *);
// 信号屏蔽
sigset_t sa_mask;
// 标识位,SA_SIGINFO表示使用sa_sigaction作为信号回调函数。
// SA_RESTART表示被信号中断的系统调用会自动重启(不重启的需要处理EINTR错误)
// SA_NOCLDSTOP使父进程在它的子进程暂停或继续运行时不会收到 SIGCHLD 信号
// SA_NOCLDWAIT使父进程在它的子进程退出时不会收到 SIGCHLD 信号,这时子进程如果退出也不会成为僵尸进程。
// SA_NODEFER:使对信号的屏蔽无效,即在信号处理函数执行期间仍能发出这个信号。
// SA_RESETHAND:信号处理之后重新设置为默认的处理方式。
int sa_flags;
void (*sa_restorer) (void);
}
// signum 指定信号编号,act新的设置信号回调的结构体,oldact 用来获取已经设置的信号的信息
int sigaction(int signum,const struct sigaction *act ,struct sigaction *oldact);
typedef struct {
unsigned long sig[_NSIG_WORDS];
} sigset_t
sigemptyset(sigset_t *set):信号集全部清0; sigfillset(sigset_t *set): 信号集全部置1,则信号集包含linux支持的64种信号; sigaddset(sigset_t *set, int signum):向信号集中加入signum信号; sigdelset(sigset_t *set, int signum):向信号集中删除signum信号; sigismember(const sigset_t *set, int signum):判定信号signum是否存在信号集中。 // SIG_BLOCK:将set指向信号集中的信号,添加到进程阻塞信号集; // SIG_UNBLOCK:将set指向信号集中的信号,从进程阻塞信号集删除 // SIG_SETMASK:将set指向信号集中的信号,设置成进程阻塞信号集 sigprocmask(int how, const sigset_t *set, sigset_t *oldset)); 不同how参数,实现不同功能 sigpending(sigset_t *set)):获取已发送到进程,却被阻塞的所有信号 sigsuspend(const sigset_t *mask)):用mask代替进程的原有掩码,并暂停进程执行,直到收到信号再恢复原有掩码并继续执行进程。
参考:
nginx中信号的处理
在main函数中会调用ngx_init_signals注册信号回调函数。
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 }
};
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));
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;
}
在调用ngx_master_process_cycle函数启动worker进程前,先阻塞设置的信号。
// 阻塞信号
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));
if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"sigprocmask() failed");
}
// 清空信号集
sigemptyset(&set);
......
// fork 工作进程
ngx_start_worker_processes(cycle, ccf->worker_processes,
NGX_PROCESS_RESPAWN);
......
// master进程最后会阻塞到该函数,等待信号的唤醒。
// 信号阻塞一直没有解除,信号只会再此处被注销执行。
sigsuspend(&set);
在fork出的子进程会调用ngx_worker_process_init函数初始化worker进程。在该函数会清楚从父进程继承的阻塞信号位。
sigemptyset(&set);
if (sigprocmask(SIG_SETMASK, &set, NULL) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"sigprocmask() failed");
}