14.5 信号
Linux提供了以信号传递进程间消息的机制,Nginx在管理master进程和worker进程时大量使用了信号。什么是信号?它是一种非常短的消息,短到只有一个数字。在中文译名中,信号相比下文将要介绍的信号量只少了一个字,但它们完全是两个概念,信号量仅用于同步代码段,而信号则用于传递消息。一个进程可以向另外一个进程或者另外一组进程发送信号消息,通知目标进程执行特定的代码。
Linux定义的前31个信号是最常用的,Nginx则通过重定义其中一些信号的处理方法来使用信号,如接收到SIGUSR1信号就意味着需要重新打开文件。使用信号时Nginx定义了一个ngx_signal_t结构体用于描述接收到信号时的行为,如下所示。
typedef struct{
//需要处理的信号
int signo;
//信号对应的字符串名称
char*signame;
//这个信号对应着的Nginx命令
char*name;
//收到signo信号后就会回调handler方法
void(*handler)(int signo);
}ngx_signal_t;
另外,Nginx还定义了一个数组,用来定义进程将会处理的所有信号。例如:
define NGX_RECONFIGURE_SIGNAL HUP
ngx_signal_t signals[]={
{ngx_signal_value(NGX_RECONFIGURE_SIGNAL),
"SIG"ngx_value(NGX_RECONFIGURE_SIGNAL),
"reload",
ngx_signal_handler},
……
}
上面的例子意味着在接收到SIGHUP信号后,将调用ngx_signal_handler方法进行处理,以便重新读取配置文件,或者说,当收到用户发来的如下命令时:
./nginx-s reload
这个新启动的Nginx进程会向实际运行的Nginx服务进程发送SIGHUP信号(执行这个命令后拉起的Nginx进程并不会重新启动服务器,而是仅用于发送信号,在ngx_get_options方法中会重置ngx_signal全局变量,而main方法中检查到其非0时就会调用ngx_signal_process方法向正在运行的Nginx服务发送信号,之后main方法就会返回,新启动的Nginx进程退出),这样运行中的服务进程也会调用ngx_signal_handler方法来处理这个信号。
在定义了ngx_signal_t类型的signals数组后,ngx_init_signals方法会初始化所有的信号,如下所示。
ngx_int_t ngx_init_signals(ngx_log_t*log)
{
ngx_signal_t*sig;
//Linux内核使用的信号
struct sigaction sa;
//遍历signals数组,处理每一个ngx_signal_t类型的结构体
for(sig=signals;sig->signo!=0;sig++){
ngx_memzero(&sa,sizeof(struct sigaction));
//设置信号的处理方法为handler方法
sa.sa_handler=sig->handler;
//将sa中的位全部置为0
sigemptyset(&sa.sa_mask);
//向Linux注册信号的回调方法
if(sigaction(sig->signo,&sa,NULL)==-1){
ngx_log_error(NGX_LOG_EMERG,log,ngx_errno,
"sigaction(%s)failed",sig->signame);
return NGX_ERROR;
}
}
return NGX_OK;
}
这样进程就可以处理信号了。如果用户希望Nginx处理更多的信号,那么可以直接向signals数组中添加新的ngx_signal_t成员。