作者:阿波
链接:http://blog.youkuaiyun.com/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网络编程>