信號是進程之間通信的另外一種方式。之前用過kill -l看了Linux系統支持的所有信號,這些信號在sys/signal.h中定義,系統支持64種信號。除了系統內核和root之外,只有具備相同uid、gid的進程才可以使用信號進行通信。
gaolu@gaolu-desktop:~$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL
5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE
9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2
13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT //本次程序提到信號
17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU
25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH
29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN
35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4
39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12
47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14
51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10
55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6
59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
gaolu@gaolu-desktop:~$
1、信號的產生
(1)kill函數
函數調用形式為int kill(pid_t pid,int sig),表示向進程pid發送信號sig。這個函數之前用過,不再介紹。
函數執行成功返回0,失敗返回-1.
(2)raise函數
該函數用于給調用進程自身發送信號,調用形式為int raise(int sig);等同與調用kill(getpid(),sig).
函數執行成功返回0,失敗返回非零數值。
(3)alarm函數
該函數用于給進程設置告警時鐘,時鐘(單位為秒)到達后,給進程發送SIGALARM信號。默認處理方式為進程直接終止運行,也可以修改捕捉信號后的默認處理函數。
【程序實例】
//連續創建5個子進程,分別給5個子進程設置告警時鐘1-5秒,時鐘到達時,捕捉到SIGALARM信號,采用默認處理方式,直接終止執行;捕捉到信號之前用pause掛起進程,使其處于等待狀態;由于捕捉信號以后的默認處理就是終止,因此不需要顯式的調用_exit等函數。
父進程等待所有的子進程結束,根據wait到的status,調用宏判斷是屬于正常退出(打印退出碼),還是接受到信號退出(打印接收到的信號值)。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void)
{
int status,i;
pid_t pid,wait_pid;
for(i=1;i<=5;i++)
{
pid = fork();
if(pid<0)
{
perror("Creat child process failed.\n");
return 1;
}
if(pid == 0)
{
printf("Child process %ld will terminal after %d seconds.\n",(long)getpid(),i);
alarm(i);
pause();
}
}
while((pid = wait(&status))&&pid!=-1)
{
if(WIFSIGNALED(status))
{
printf("Process %d exit because of receiving signal %d.\n",pid,WTERMSIG(status));
}
if(WIFEXITED(status))
{
printf("Process %d exit code %d.\n",pid,WEXITSTATUS(status));
}
}
return 0;
}
執行結果:
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$ gcc -o sig systemcall2.c
gaolu@gaolu-desktop:~$ ./sig
Child process 5757 will terminal after 1 seconds.
Child process 5758 will terminal after 2 seconds.
Child process 5759 will terminal after 3 seconds.
Child process 5760 will terminal after 4 seconds.
Child process 5761 will terminal after 5 seconds.
Process 5757 exit because of receiving signal 14. //14對應信號SIGALRM
Process 5758 exit because of receiving signal 14.
Process 5759 exit because of receiving signal 14.
Process 5760 exit because of receiving signal 14.
Process 5761 exit because of receiving signal 14.
gaolu@gaolu-desktop:~$
2、信號的處理
進程捕捉到信號以后可以有三種處理方式:
---不做任何處理(采用系統默認的處理方式);
---忽略信號(需要修改默認處理到ignore);
---捕獲信號(需要修改默認處理到自定義的處理方式);
*******************************************
SIGKILL和SIGSTOP是不能夠被捕獲的。
*******************************************
下面signal函數和sigaction函數是Linux提供的2個系統調用。
(1)signal函數
調用形式sighandler_t signal(int signum,sighandler_t handler);
typedef void (*sighandler_t)(int);
signum:要捕獲的信號值;
handler:指向要調用的函數指針(可以為SIG_IGN忽略信號或者為SIG_DFL采用默認方式);
【程序實例】
代碼中修改了對SIGINT的處理方式,默認為直接終止,修改成為忽略信號;對于SIGUSR1,調用函數sigusr1 進行處理后結束。
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
void sigusr1 (int sig)
{
printf("Get the SIGUSER signal,now process...\n");
exit(0);
}
int main (void)
{
if(signal(SIGINT,SIG_IGN)==SIG_ERR)
{
printf("Fail to reset signal handler of SIGINT.\n");
return 1;
}
if(signal(SIGUSR1,sigusr1)==SIG_ERR)
{
printf("Fail to reset signal handler of SIGUSR1.\n");
return 1;
}
pause();
return 0;
}
執行結果:
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$ gcc -o sig systemcall2.c
gaolu@gaolu-desktop:~$ ./sig &
[1] 6011
gaolu@gaolu-desktop:~$ kill -SIGINT 6011
gaolu@gaolu-desktop:~$ jobs
[1]+ Running ./sig & //SIGINT被忽略了
gaolu@gaolu-desktop:~$ kill -SIGUSR1 6011
Get the SIGUSER signal,now process...
[1]+ Done ./sig
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$ jobs
gaolu@gaolu-desktop:~$
(2)sigaction函數
該函數用于檢查或者指定信號的處理動作。
調用形式int sigaction(int signum,const struct sigaction *act,const struct sigaction *oldact);
---signum:要捕捉的信號值(不包括SIGKILL和SIGSTOP);
---act:如果act非空,則按照act指向的結構體信息重新設置信號處理函數;
---oldact:如果oldact非空,將原有的信號處理函數保存在oldact指向的結構體。
sigaction結構體定義和參數取值略。。。
【程序實例】
下面的程序打印信號1-11的處理方式,由于只有信號10(SIGUSR1)的默認處理方式被函數sigaction修改過,其他的打印都為默認處理SIG_DFL.
#include <stdio.h>
#include <signal.h>
void signal_process_info(struct sigaction *act)
{
switch(act->sa_flags)
{
case (int)SIG_DFL:
{
printf("using default handler.\n");
break;
}
case (int)SIG_IGN:
{
printf("ignore the signal.\n");
break;
}
default:
{
printf("use handler: %0x.\n",act->sa_flags);
break;
}
}
return;
}
void usr1_handler(void)
{
}
int main(void)
{
int i;
struct sigaction act,oldact;
act.sa_handler = usr1_handler;
act.sa_flags = SA_NODEFER|SA_RESETHAND; //SA_RESETHAND:捕獲信號完成處理后,將信號處理恢復為默認處理;SA_NODEFER:完成信號處理之前如果再次收到信號,不做處理
sigaction(SIGUSR1,&act,&oldact); //對信號SIGUSR1更改默認處理方式,處理調用函數usr1_handler
for(i=1;i<12;i++)
{
printf("signal %d handler is:",i);
sigaction(i,NULL,&oldact); //act=NULL,檢查而已
signal_process_info(&oldact); //打印信號處理方式
}
return 0;
}
執行結果:
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$ gcc -o sig2 systemcall2.c
systemcall2.c: In function main
systemcall2.c:37: warning: assignment from incompatible pointer type
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$ ./sig2
signal 1 handler is:using default handler.
signal 2 handler is:using default handler.
signal 3 handler is:using default handler.
signal 4 handler is:using default handler.
signal 5 handler is:using default handler.
signal 6 handler is:using default handler.
signal 7 handler is:using default handler.
signal 8 handler is:using default handler.
signal 9 handler is:using default handler.
signal 10 handler is:use handler: c0000000.
signal 11 handler is:using default handler.
gaolu@gaolu-desktop:~$
*************************************************************************************
信號值小于SIGRTMIN的信號都是不可靠信號。對于UNIX系統而言,不可靠信號的含義有2個:
(1)可能對信號采用錯誤的處理
捕捉到不可靠信號并處理以后,可能將信號的處理方法恢復到系統默認的處理方法。因此每次處理以后,都要重新設置信號處理函數。
(2)存在信號丟失的可能
比如sleep()或者pause()時可能捕捉不到信號。
在Linux系統下,不可靠信號僅指信號的丟失,因為系統對不可靠信號機制做了改進,不需要重新設置信號處理函數。
考慮到代碼的可移植性,使用較多的是sigaction()函數。