作者:阿波
链接: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数组,其信号的handler为ngx_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);
};
该定义从sigaction的manual页而来,如果查看kernel源代码,可能因版本不同有所调整。
2.几个问题
2.1ngx_signal_value宏是如何得到整数的信号值signo的?
举个例子,NGX_RECONFIGURE_SIGNAL=HUP,因此ngx_signal_value(NGX_RECONFIGURE_SIGNAL)=SIGHUP。
从上述signals数组可以看出,SIGHUP的signo=1,name为"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=31和SISPIPE=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的情况,因此可用来区别于别的指针。
实际上,对nginx的31号和13号信号,sigaction将SIG_IGN=0x1注册(登记)为其signal的handler。即将这两个信号交给系统(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网络编程>