作者:阿波
連結: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數組,其訊號的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 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=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