Open vislee opened 7 years ago
通过向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代替进程的原有掩码,并暂停进程执行,直到收到信号再恢复原有掩码并继续执行进程。
参考:
在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"); }
概述
通过向nginx的master进程发送信号来管理nginx。例如:向nginx进程发送sighup信号重新加载配置。操作:kill -1 pid 或者nginx -s reload。
知识回顾
linux信号是进程间通信的一种方式,信号是一种软中断,实际上也属于进程控制的一部分。 软中断信号signal用来通知进程发生了异步事件,进程之间通过kill系统调用发送软中断,内核也会因内部事件而给进程发送信号,通知发生了某个事件。信号仅通知发生了什么事件,并不给进程传递数据。
收到信号的进程可以有3种方式来处理,1: 进程通过signal等函数指定对应的处理函数。2: 忽略信号。3: 系统默认的处理方式。
实际执行信号的处理动作称为信号递达(delivery),信号从产生到递达之间的状态称为信号未决(pending)。进程可以选择阻塞信号,被阻塞的信号产生时保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。sigsuspend函数可以临时解除信号阻塞。
下面是信号相关的函数:
注册信号回调函数
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_master_process_cycle函数启动worker进程前,先阻塞设置的信号。
在fork出的子进程会调用ngx_worker_process_init函数初始化worker进程。在该函数会清楚从父进程继承的阻塞信号位。