在nginx启动完成后,主进程会进入一个死循环,等待着信号进行退出、重新加载配置文件、热代码替换等一系列操作,每一段动作对应着一个全局变量,当在死循环中轮询发现变量值为1时,程序就会执行相应的动作。但这个全局变量的值是在哪里改变的呢?
实际上每一个全局变量都是和某种信号对应的,系统在处理信号的时候改变了全局变量的值,最终通过信号完成对系统的控制。nginx将所有支持的信号通过全局数组signals的形式包装了起来,数组元素是ngx_signal_t类型的,每一个元素对应着一种信号,也定义了信号发生时的处理函数。代码里,所有的信号都只对应同一个信号处理函数ngx_signal_handler,此函数的主要作用是将与信号对应的全局变量值变为1。
signals数组是在ngx_init_signals函数中被初始化的,在这个函数中,依次为数组中的元素调用了一次信号注册函数sigaction,将每种信号的处理函数告诉系统。这时只要等待外界发送信号就可以执行对应的处理函数了。那操作员又是如何对nginx系统发送信号的呢?
仔细观察nginx的main函数,我们可以留意到这样一条语句
这个语句的大概意思是,如果检测到有信号传入,那么就调用ngx_signal_process函数后返回,由于子进程得到后面才能创建,在这里返回实际上就相当于退出程序了,可能不太明白了,nginx都退出了,还怎么用。其实退出的并不是真正提供服务的程序,只是操作员临时起的nginx程序,为的是向真正提供服务的nginx发送控制信号,参数ngx_signal就是操作员起临时程序传进来的信号名称。对用户来说,nginx只接受四种信号stop、quit、reopen、reload,其他的信号只能是程序内部产生。
我们再观察ngx_signal_process函数
函数的前一部分是通过读取配置信息,打开一个文件,并获取其中的内容。这个文件记录了当前提供服务的nginx主进程的pid值,顺便说一句,nginx采用多进程服务器模型,有主进程和处理网络事件的子进程,而这个函数只是获取主进程的pid值,为的是向主进程发送信号。函数的后半部分调用了ngx_os_signal_process后立即返回结束了整个流程,在调用ngx_os_signal_process时,程序将前半部分获得的主程序pid值与信号名字都传给了它,而它的作用正是完成最后发送信号的任务,我们也进入到这最后一步,
函数很简短,依次比较signals数组中信号元素的名字,如果匹配上则通过kill函数向程序发送真正的信号,kill是系统函数,这里不多说。
操作员也就是用户向主进程发送信号的时候,主进程会把信号依次传递给下面的子进程,最终也是通过kill函数,到这里用户才通过信号实现对整个服务的把控。
网友评论