nginx源码分析—信号初始化

晨曦之光 发布于 2012/03/09 14:12
阅读 259
收藏 0

作者:阿波
链接: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_t
ngx_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 SIGUSR1		10
#define SIGSEGV		11
#define SIGUSR2		12
#define SIGPIPE		13
#define SIGALRM		14
#define SIGTERM		15
#define SIGSTKFLT		16
#define SIGCHLD		17
#define SIGCONT		18
#define SIGSTOP		19
#define SIGTSTP		20
#define SIGTTIN		21
#define SIGTTOU		22
#define SIGURG		23
#define SIGXCPU		24
#define SIGXFSZ		25
#define SIGVTALRM		26
#define SIGPROF		27
#define SIGWINCH		28
#define SIGIO			29
#define SIGPOLL		SIGIO
/*
#define SIGLOST		29
*/
#define SIGPWR		30
#define SIGSYS		31
#define	SIGUNUSED	31

/* These should not be considered constants from userland.  */
#define SIGRTMIN		32
#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


原文链接:http://blog.csdn.net/livelylittlefish/article/details/7308100
加载中
返回顶部
顶部