首先說明我的系統,CentOS 6.6,內核為2.6.32-504.12.2.el6.i686。
當用signal對某個信號設定信號處理函數的時候,有些信號的處理函數會被重置,有些則不會,這種情況的具體說明我還沒有找到,這里我就先列一下我找到的幾個信號。
信號處理程序會被重置的信號:
1. SIGALRM
比如下面這段代碼,這段代碼的作用就是給自己發送SIGALRM信號,直到發送了NUM次。
1 #include <errno.h> 2 #include <pwd.h> 3 #include <signal.h> 4 #include <string.h> 5 #include <stdlib.h> 6 #include <stdarg.h> 7 #include <stdio.h> 8 #include <sys/types.h> 9 #include <unistd.h> 10 11 #define BUFSIZE 512 12 #define NUM 5 13 14 /* 15 * 這三個函數是我自定義的,功能就是利用strerror打印errno的信息,並且退出 16 */ 17 void err_exit(char *fmt,...); 18 int err_dump(char *fmt,...); 19 int err_ret(char *fmt,...); 20 21 int alrm_count = 0; //對發送的alrm信號進行計數 22 /* 23 * 本函數用來處理SIGALRM信號 24 */ 25 void sig_alrm(int signo) 26 { 27 alrm_count++; 28 printf("In signal SIGALRM handler\n"); 29 if(SIG_ERR == signal(SIGALRM,sig_alrm)) 30 err_exit("[signal]: "); 31 if(alrm_count < NUM) { 32 alarm(1); 33 pause(); 34 } 35 } 36 37 int main(int argc,char *argv[]) 38 { 39 if(SIG_ERR == signal(SIGALRM,sig_alrm)) 40 err_exit("[signal]: "); 41 42 /*alarm函數的功能就是在1s之后向本進程發送一個SIGALRM信號*/ 43 alarm(1); 44 pause(); 45 46 return 0; 47 }
這個程序的29~30行就是在信號的處理函數中重新設置對SIGALRM的處理函數,下次產生SIGALRM這個信號的時候,繼續來調用這個處理函數。程序的運行結果如下:
如果沒有29~30行那兩行的內容,程序的運行結果就會變成下面這樣:
第一次接受到SIGALRM信號,程序會調用我們自定義的sig_alrm信號處理函數,但是第二次接受到SIGALRM信號的時候,程序就會調用系統默認的SIGALRM的信號處理函數了,此時就會打印出Alarm clock信息。
2. SIGCHLD信號(SIGCLD)
在linux下面,這兩個信號是等價的,所以就放在一起來討論。首先先把代碼貼上來:
1 #include <errno.h> 2 #include <signal.h> 3 #include <string.h> 4 #include <stdlib.h> 5 #include <stdarg.h> 6 #include <stdio.h> 7 #include <sys/types.h> 8 #include <sys/wait.h> 9 #include <unistd.h> 10 11 #define BUFSIZE 512 12 #define NUM 2 13 14 void err_exit(char *fmt,...); 15 int err_dump(char *fmt,...); 16 int err_ret(char *fmt,...); 17 18 void sig_chld(int signo); 19 20 int main(int argc,char *argv[]) 21 { 22 pid_t pid; 23 24 if(SIG_ERR == signal(SIGCHLD,sig_chld)) 25 perror("[signal]: "); 26 27 for(int loop=0;loop<NUM;loop++) { 28 if(-1 == (pid=fork())) { 29 err_exit("[fork]:"); 30 } else if(0 == pid) { 31 printf("I'm the No.%d Child %d\n",loop+1,getpid()); 32 return 0; 33 } else { 34 pause(); 35 } 36 } 37 38 return 0; 39 } 40 41 void sig_chld(int signo) 42 { 43 int status; 44 pid_t cpid; 45 46 /* printf("A child process terminated!\n"); */ 47 if(-1 == (cpid=wait(&status))) 48 err_exit("[wait]: "); 49 else 50 printf("Process %d terminated!\n",cpid); 51 52 if(SIG_ERR == signal(SIGCHLD,sig_chld)) 53 perror("[signal]: "); 54 }
這段代碼的作用就是父進程創建一個子進程,然后暫停,等待子進程結束發送SIGCHLD信號過來,在SIGCHLD信號的信號處理函數中,將子進程回收,並且打印出回收的子進程進程id。執行完這些步驟之后,再來繼續這個過程,直到循環了NUM次。
這個程序的運行結果如下所示:
這里可以看到,兩個子進程結束時發送的SIGCHLD信號都被接受到了,並且兩個子進程都被回收了。
如果我去掉了在信號處理函數中的signal函數(52~53行),那么程序的運行結果就會如下圖所示:
主函數卡死了,此時我用top命令來查看這進程信息,發現第二個子進程變成了僵屍進程,如下圖所示:
此時SIGCHLD信號處理函數,變成了默認的忽略(SIG_IGN),接受到了SIGCHLD信號就不會調用函數了,而pause函數的說明則是這樣的:
pause函數只有遇到讓主進程終止的信號,或者是產生信號處理函數調用的函數才會停止睡眠。而在上面的程序中,第二次的時候SIGCHLD信號由於采用的系統默認的配置SIG_IGN,此時不會產生信號處理函數的調用,所以主進程就繼續暫停。
信號處理程序不會被重置的信號:
目前我只發現了兩個,就是兩個用戶自定義的函數,SIG_USR1和SIG_USR2,具體可以參看下面這段代碼:
1 #include<errno.h> 2 #include<signal.h> 3 #include<string.h> 4 #include<stdlib.h> 5 #include<stdarg.h> 6 #include<stdio.h> 7 #include<unistd.h> 8 9 #define BUFSIZE 512 10 11 /* 12 * 這三個函數是我自定義的,功能就是利用strerror打印errno的信息,並且退出 13 */ 14 void err_exit(char *fmt,...); 15 void err_dump(char *fmt,...); 16 int err_ret(char *fmt,...); 17 18 void sig_handler(int signo) 19 { 20 if(signo == SIGUSR1) 21 printf("Catch the SIGUSR1 [%d]\n",signo); 22 else if(signo == SIGUSR2) 23 printf("Catch the SIGUSR2 [%d]\n",signo); 24 else 25 err_dump("Catch the signal %d\n",signo); 26 } 27 int main(int argc,char *argv[]) 28 { 29 if(SIG_ERR == signal(SIGUSR1,sig_handler)) 30 err_exit("[signal]1: "); 31 if(SIG_ERR == signal(SIGUSR2,sig_handler)) 32 err_exit("[signal]2: "); 33 34 for(;;) 35 pause(); 36 37 return 0; 38 }
這個程序就是一直等待信號發送過來,並且會對SIGUSR1和SIGUSR2進行處理,其他信號則會執行系統默認的處理情況。運行的結果如下圖:
程序開始后,我通過另一個終端給這個程序發送信號,如下圖所示:
而程序運行的界面如下圖所示:
從這里可以看出,我們只對SIGUSR這兩個函數設置了一次信號處理函數,但是它們的處理方式就不會被重置,發送多次SIGUSR信號都是同一種處理方式。