作者:阿波
链接: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网络编程>
http://www.cplusplus.com/reference/clibrary/csignal/signal