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成员。