信号是进程之间通信的另外一种方式。之前用过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
#include
#include
#include
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
#include
#include
#include
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
#include
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()函数。
**************************************************************************************