原文來自靜雅齋,轉載請註明出處。javascript
從前面的文章和實際使用中,你們應該也對信號有一些模糊的認識了。好比,Nginx
使用信號來管理進程的啓動關閉,UNIX進程信號是經典的操做異步事件機制。在早期,Unix信號是每一個實現都不一樣的,可是隨着Unix標準化的進行,標準提出了統一的信號機制模型。除了共有的信號之外,各自的實現還提供了擴展信號,固然,這個不是重點。
在頭文件<signal.h>
中定義了全部的信號,咱們能夠經過編輯這個頭文件來查看當前Unix實現提供的信號。java
#define SIGHUP 1 /* hangup */
#define SIGINT 2 /* interrupt */
#define SIGQUIT 3 /* quit */
#define SIGILL 4 /* illegal instruction (not reset when caught) */
#define SIGTRAP 5 /* trace trap (not reset when caught) */
#define SIGABRT 6 /* abort() */
...複製代碼
就是這樣列出來的,從上面能夠看到,信號不存在爲0的編號,在最初的幾章裏,提到過產生信號的條件shell
信號是進程交互和異步事件的方式,因爲信號出現是不可知的,因此不能說像檢查返回值,檢查 errno 同樣判斷是否有信號,系統提供了一套完善的機制來實現,開發者只須要讓進程註冊函數來處理信號。
進程收到信號後,能夠選擇3種方式處理行爲:windows
下面列出了一般的信號安全
No Name Default Action Description
1 SIGHUP terminate process terminal line hangup
2 SIGINT terminate process interrupt program
3 SIGQUIT create core image quit program
4 SIGILL create core image illegal instruction
5 SIGTRAP create core image trace trap
6 SIGABRT create core image abort program (formerly SIGIOT)
7 SIGEMT create core image emulate instruction executed
8 SIGFPE create core image floating-point exception
9 SIGKILL terminate process kill program
10 SIGBUS create core image bus error
11 SIGSEGV create core image segmentation violation
12 SIGSYS create core image non-existent system call invoked
13 SIGPIPE terminate process write on a pipe with no reader
14 SIGALRM terminate process real-time timer expired
15 SIGTERM terminate process software termination signal
16 SIGURG discard signal urgent condition present on socket
17 SIGSTOP stop process stop (cannot be caught or ignored)
18 SIGTSTP stop process stop signal generated from keyboard
19 SIGCONT discard signal continue after stop
20 SIGCHLD discard signal child status has changed
21 SIGTTIN stop process background read attempted from control terminal
22 SIGTTOU stop process background write attempted to control terminal
23 SIGIO discard signal I/O is possible on a descriptor (see fcntl(2))
24 SIGXCPU terminate process cpu time limit exceeded (see setrlimit(2))
25 SIGXFSZ terminate process file size limit exceeded (see setrlimit(2))
26 SIGVTALRM terminate process virtual time alarm (see setitimer(2))
27 SIGPROF terminate process profiling timer alarm (see setitimer(2))
28 SIGWINCH discard signal Window size change
29 SIGINFO discard signal status request from keyboard
30 SIGUSR1 terminate process User defined signal 1
31 SIGUSR2 terminate process User defined signal 2複製代碼
void (*signal(int sig, void (*func)(int)))(int);
or in the equivalent but easier to read typedef'd version:
typedef void (*sig_t) (int);
sig_t signal(int sig, sig_t func);複製代碼
Unix使用信號的接口就是signal
函數。在前面的章節中提到過signal函數的定義和具體意義。signal函數其實是ISO C定義的,可是因爲這種跨平臺函數具備實現的不一樣,因此windows仍是Unix各個實現,實際上都有其特殊的方式。對於函數自己來講,帶有兩個參數,一個是signo信號名,func的值是一個常亮多線程
1 SIGHUP terminate process terminal line hangup
2 SIGINT terminate process interrupt program
3 SIGQUIT create core image quit program
4 SIGILL create core image illegal instruction
5 SIGTRAP create core image trace trap
6 SIGABRT create core image abort program (formerly SIGIOT)
7 SIGEMT create core image emulate instruction executed
8 SIGFPE create core image floating-point exception
9 SIGKILL terminate process kill program
10 SIGBUS create core image bus error
11 SIGSEGV create core image segmentation violation
12 SIGSYS create core image non-existent system call invoked
13 SIGPIPE terminate process write on a pipe with no reader
14 SIGALRM terminate process real-time timer expired
15 SIGTERM terminate process software termination signal
16 SIGURG discard signal urgent condition present on socket
17 SIGSTOP stop process stop (cannot be caught or ignored)
18 SIGTSTP stop process stop signal generated from keyboard
19 SIGCONT discard signal continue after stop
20 SIGCHLD discard signal child status has changed
21 SIGTTIN stop process background read attempted from control terminal
22 SIGTTOU stop process background write attempted to control terminal
23 SIGIO discard signal I/O is possible on a descriptor (see fcntl(2))
24 SIGXCPU terminate process cpu time limit exceeded (see setrlimit(2))
25 SIGXFSZ terminate process file size limit exceeded (see setrlimit(2))
26 SIGVTALRM terminate process virtual time alarm (see setitimer(2))
27 SIGPROF terminate process profiling timer alarm (see setitimer(2))
28 SIGWINCH discard signal Window size change
29 SIGINFO discard signal status request from keyboard
30 SIGUSR1 terminate process User defined signal 1
31 SIGUSR2 terminate process User defined signal 2
If a process explicitly specifies SIG_IGN as the action for the signal SIGCHLD, the system will not create zombie processes when children of the callingprocess exit. As a consequence, the system will discard the exit status from the child processes. If the calling process subsequently issues a call to wait(2) or equivalent, it will block until all of the calling process's children terminate, and then return a value of -1 with errno set to ECHILD.複製代碼
上面這些是蘋果系統列出的可以使用的信號,若是func參數能夠指定3種,分別是常量SIG_IGN、常量SIG_DFL或者一個函數指針。常量SIG_IGN表示內核忽略此信號,常量SIG_DFL表示執行內核默認動做,函數指針則是當信號發生時,調用該函數,通常叫作信號處理函數。
結合前文中對signal函數的理解,實際上signal函數要求兩個參數,只有一個返回值,signal函數第一個參數是signo信號名,第二個參數是一個函數指針,通常是信號處理函數,最後返回這個信號處理函數指針。因爲函數原型很是複雜,通常都是使用typedef將其簡化。異步
#define SIG_DFL (void (*)(int))0
#define SIG_IGN (void (*)(int))1
#define SIG_HOLD (void (*)(int))5
#define SIG_ERR ((void (*)(int))-1)複製代碼
通常聲明就是像上面同樣。下面是一個例程socket
#include "include/apue.h"
static void sig_usr(int);
int main(int argc, char *argv[])
{
if (signal(SIGUSR1, sig_usr) == SIG_ERR)
err_sys("can't catch SIGUSR1");
if (signal(SIGUSR2, sig_usr) == SIG_ERR)
err_sys("can't catch SIGUSR2");
for ( ; ; )
pause();
}
static void sig_usr(int signo)
{
if (signo == SIGUSR1)
printf("received SIGUSR1\n");
else if (signo == SIGUSR2)
printf("received SIGUSR2\n");
else
err_dump("received signal %d\n", signo);
}複製代碼
而後編譯運行函數
> ./a.out &
[1] 21588
> kill -USR1 21588
received SIGUSR1
> kill -USR2 21588
received SIGUSR2
> kill 21588
[1] + 21588 terminated ./a.out複製代碼
咱們知道,全部的進程都是內核啓動的,當啓動一個進程的時候,全部的信號都會被設置爲系統默認或者忽略,在Unix環境中最典型的一個例子就是shell啓動其餘進程,爲了保證前臺和後臺對中斷信號和退出信號的捕捉,通常都這樣寫學習
void sig_int(int), sig_quit(int);
if (signal(SIGINT, SIG_IGN) != SIG_IGN)
signal(SIGINT, sig_int);
if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
signal(SIGQUIT, sig_quit);複製代碼
由於全部都是被子進程繼承的,因此在exec時,父進程能夠控制子進程的忽略狀態,因此須要這樣判斷。
在上面能夠看出,signal函數除了改變當前信號的處理方式之外,還會返回以前的狀態,這就很是的繁瑣,因此在實際中不多使用了。
信號是異步調用,進程自身執行各類正常指令序列,當接受到信號的時候,內核就會通知進程處理,而此時正常的指令序列就會被中斷,很容易形成進程空間的破壞,因此如今的Unix實現都會提供在信號處理程序中保證安全調用的函數,通俗的說,就是異步信號安全。至於具體列表,則須要查看具體Unix實現。
在前面學習errno錯誤信息的時候講到過,每一個進程都維護了一個errno變量,當須要得到的時候就經過extern int errno
的形式獲取。固然實際不是這樣的,由於有多線程的模型存在,因此實際狀況更加複雜。當進程執行正常代碼,而後調用的系統函數修改了errno值,若是一個信號被髮送給進程,進程的信號處理函數調用了另外一個系統函數修改errno值,結果最終返回的時候就是信號處理程序內調用獲得的errno,因此在實際開發中,若是須要調用系統函數,須要先保存errno值,而後調用後恢復errno