首先說明我的系統,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 51212 #define NUM 513 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 51212 #define NUM 213 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 else50 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 51210 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 else25 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信號都是同一種處理方式。
新聞熱點
疑難解答