nginx源码分析—信号初始化

来源:互联网 发布:nba所有状元体测数据 编辑:程序博客网 时间:2024/05/05 21:48

作者:阿波
链接:http://blog.csdn.net/livelylittlefish/article/details/7308100

Content

0.

1. ngx_init_signals()函数

1.1 ngx_signal_t结构

1.2 signals数组

1.3 sigaction结构

2.几个问题

2.1ngx_signal_value宏是如何得到整数的信号值signo的?

2.2 handler=SIG_IGN=0x1是如何忽略信号的?

3.ngx_signal_handler()函数

4.小结

 

0.

本文主要分析nginx信号初始化及其处理。文中如无特别说明,.表示nginx-1.0.4代码目录,本文为/usr/src/nginx-1.0.4

1. ngx_init_signals()函数

该函数主要任务是设置signals[]数组中每个信号的action(即常说的注册、安装等)。如下。

./src/os/unix/ngx_process.c

ngx_int_tngx_init_signals(ngx_log_t *log){    ngx_signal_t      *sig;    struct sigaction   sa;    for (sig = signals; sig->signo != 0; sig++) {   /* signals数组 */        ngx_memzero(&sa, sizeof(struct sigaction)); /* 此处sigaction是一个结构类型 */        sa.sa_handler = sig->handler;        sigemptyset(&sa.sa_mask);    /* 清空sa_mask */        if (sigaction(sig->signo, &sa, NULL) == -1) { /* 设置sig->signo信号的action,此处sigaction为系统API */            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,                          "sigaction(%s) failed", sig->signame);            return NGX_ERROR;        }    }    return NGX_OK;}

1.1 ngx_signal_t结构

nginx的信号结构如下。

typedef struct {    int     signo;                /* 信号值 */    char   *signame;              /* 信号名 */    char   *name;                 /* 信号可读名 */    void  (*handler)(int signo);  /* 信号处理程序 */} ngx_signal_t;

nginx进程收到相关信号时就会执行注册的handler

1.2 signals数组

对于该函数中的signals数组,其信号的handlerngx_signal_handler(),如下所示。

./src/os/unix/ngx_process.c

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", "", SIG_IGN },    /* SIGSYS=31,该信号handler=SIG_IGN,表示忽略该信号 */    { SIGPIPE, "SIGPIPE, SIG_IGN", "", SIG_IGN },  /* SIGPIPE=13,该信号handler=SIG_IGN,表示忽略该信号 */     { 0, NULL, "", NULL }};

通过调试nginx,可以查看在运行环境中该数组的真实内容,也可看出ngx_signal_t结构,及nginx支持的信号种类。如下。

(gdb) p signals$5 = {{    signo = 1,     signame = 0x476235 "SIGHUP",     name = 0x4726ab "reload",     handler = 0x41df10 <ngx_signal_handler>  }, {    signo = 10,     signame = 0x47623c "SIGUSR1",     name = 0x4726a4 "reopen",     handler = 0x41df10 <ngx_signal_handler>  }, {    signo = 28,     signame = 0x476244 "SIGWINCH",     name = 0x47b68f "",     handler = 0x41df10 <ngx_signal_handler>  }, {    signo = 15,     signame = 0x47624d "SIGTERM",     name = 0x47269a "stop",     handler = 0x41df10 <ngx_signal_handler>  }, {    signo = 3,     signame = 0x476255 "SIGQUIT",     name = 0x47269f "quit",     handler = 0x41df10 <ngx_signal_handler>  }, {    signo = 12,     signame = 0x47625d "SIGUSR2",     name = 0x47b68f "",     handler = 0x41df10 <ngx_signal_handler>  }, {    signo = 14,     signame = 0x476265 "SIGALRM",     name = 0x47b68f "",     handler = 0x41df10 <ngx_signal_handler>  }, {    signo = 2,     signame = 0x47626d "SIGINT",     name = 0x47b68f "",     handler = 0x41df10 <ngx_signal_handler>  }, {    signo = 29,     signame = 0x476274 "SIGIO",     name = 0x47b68f "",     handler = 0x41df10 <ngx_signal_handler>  }, {    signo = 17,     signame = 0x47627a "SIGCHLD",     name = 0x47b68f "",     handler = 0x41df10 <ngx_signal_handler>  }, {    signo = 31,     signame = 0x476282 "SIGSYS, SIG_IGN",     name = 0x47b68f "",     handler = 0x1      /* 该信号handler=SIG_IGN=0x1,表示忽略该信号 */  }, {    signo = 13,     signame = 0x476292 "SIGPIPE, SIG_IGN",     name = 0x47b68f "",     handler = 0x1      /* 该信号handler=SIG_IGN=0x1,表示忽略该信号 */  }, {    signo = 0,     signame = 0x0,     name = 0x47b68f "",     handler = 0  }}

通过调试打印出signals数组的内容,可以很清晰地看到其定义。几个用到的宏如下。

./src/core/ngx_config.h

#define ngx_signal_helper(n)     SIG##n#define ngx_signal_value(n)      ngx_signal_helper(n)#define NGX_SHUTDOWN_SIGNAL      QUIT#define NGX_TERMINATE_SIGNAL     TERM#define NGX_NOACCEPT_SIGNAL      WINCH#define NGX_RECONFIGURE_SIGNAL   HUP#if (NGX_LINUXTHREADS)#define NGX_REOPEN_SIGNAL        INFO#define NGX_CHANGEBIN_SIGNAL     XCPU#else#define NGX_REOPEN_SIGNAL        USR1#define NGX_CHANGEBIN_SIGNAL     USR2#endif

1.3 sigaction结构

sigaction结构定义如下。

struct sigaction {    void     (*sa_handler)(int);    void     (*sa_sigaction)(int, siginfo_t *, void *);    sigset_t   sa_mask;    int        sa_flags;    void     (*sa_restorer)(void);};

该定义从sigactionmanual页而来,如果查看kernel源代码,可能因版本不同有所调整。

 

2.几个问题

2.1ngx_signal_value宏是如何得到整数的信号值signo的?

 

举个例子,NGX_RECONFIGURE_SIGNAL=HUP,因此ngx_signal_value(NGX_RECONFIGURE_SIGNAL)=SIGHUP

从上述signals数组可以看出,SIGHUPsigno=1name"reload"。那么,这个1是在哪里定义的?

——这很容易能想到kernel源代码。果期不然,在#include <signal.h>

 

file:/usr/include/asm/signal.h/usr/include/asm-generic/signal.h均有定义。

#define SIGHUP 1#define SIGINT 2#define SIGQUIT 3#define SIGILL 4#define SIGTRAP 5#define SIGABRT 6#define SIGIOT 6#define SIGBUS 7#define SIGFPE 8#define SIGKILL 9#define SIGUSR110#define SIGSEGV11#define SIGUSR212#define SIGPIPE13#define SIGALRM14#define SIGTERM15#define SIGSTKFLT16#define SIGCHLD17#define SIGCONT18#define SIGSTOP19#define SIGTSTP20#define SIGTTIN21#define SIGTTOU22#define SIGURG23#define SIGXCPU24#define SIGXFSZ25#define SIGVTALRM26#define SIGPROF27#define SIGWINCH28#define SIGIO29#define SIGPOLLSIGIO/*#define SIGLOST29*/#define SIGPWR30#define SIGSYS31#defineSIGUNUSED31/* These should not be considered constants from userland.  */#define SIGRTMIN32#define SIGRTMAX_NSIG

2.2 handler=SIG_IGN=0x1是如何忽略信号的?

从上述signals数组中可以看出,SIGSYS=31SISPIPE=13信号,其handler=SIG_IGN=0x1,表明忽略该信号。是如何做到的?SIG_IGN又是在何处定义的?

 

file: /usr/include/asm-generic/signal-defs.h

#ifndef SIG_BLOCK#define SIG_BLOCK          0/* for blocking signals */#endif#ifndef SIG_UNBLOCK#define SIG_UNBLOCK        1/* for unblocking signals */#endif#ifndef SIG_SETMASK#define SIG_SETMASK        2/* for setting the signal mask */#endif#ifndef __ASSEMBLY__typedef void __signalfn_t(int);typedef __signalfn_t __user *__sighandler_t;typedef void __restorefn_t(void);typedef __restorefn_t __user *__sigrestore_t;#define SIG_DFL((__force __sighandler_t)0)/* default signal handling */#define SIG_IGN((__force __sighandler_t)1)/* ignore signal */#define SIG_ERR((__force __sighandler_t)-1)/* error return from signal */#endif

即,

#defineSIG_IGN((void (*)(int))1)

handler函数类型为void(*)(int),符合sigaction结构中sa_handler定义。表明信号忽略函数地址为1,而在实际中是不可能出现函数地址为1的情况,因此可用来区别于别的指针。

实际上,对nginx31号和13号信号,sigactionSIG_IGN=0x1注册(登记)为其signalhandler。即将这两个信号交给系统(init进程)处理。

另:忽略SIGCHLD信号,常作为提高并发服务器性能的一个技巧。因为并发服务器可能fork很多子进程,子进程终结后需要服务器进程wait子进程并清理资源。如果将该信号忽略,可使内核把僵尸子进程交给init进程处理,节省大量僵尸子进程占用的系统资源。

 

3.ngx_signal_handler()函数

该函数仅根据其收到的信号对相应的全局变量,如ngx_quit, ngx_terminate, ngx_noaccept等进行赋值(均赋值为1),当该进程发现相应变量为1时,即会采取相应的操作。

具体的处理,可参考源代码。

 

4.小结

本文主要分析nginx启动过程中信号如何初始化。

 

Reference

# man sigaction

# man -S 7 signal

# man -S 2 kill

<Unix网络编程>

http://www.cplusplus.com/reference/clibrary/csignal/signal

原创粉丝点击