之前在看APUE的时候,就没有看信号这一章,现在APUE的书还给图书馆了,换了一本linux C程序设计,这本书虽然比APUE还广泛,可是和APUE比起来还是有差距,中国人老想着用文字去说明问题,不用例子去说明,而且例子不能运行的,例子和例子之间千丝万缕的关系的 还是有很多的。总之,我觉得Linux C程序设计还有很多能改进的地方,至少风格,示例是可以好好改进一下的
现在把信号简单补充一下:
[root@server myprogram]# 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     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
可见,我这里有64个信号,并且从1开始排序
那么对于信号的处理是怎么一回事情呢?其实,进程中对于信号的处理是异步的,不管进程在干什么,那么是个死循环,也会去响应信号,去做信号处理函数
譬如:
int main()
{
     while(1) ;
     return 0;
}
[root@server myprogram]# gcc -o test test.c
[root@server myprogram]# ./test
当我们按下Ctrl+c的时候,进程终止,这是进程响应SIGINT信号的结果
为了更明白一点,我们这样做
[root@server myprogram]# gcc -o test test.c
[root@server myprogram]# ./test &
[1] 1790
[root@server myprogram]# kill -SIGSEGV 1790
[root@server myprogram]# 
[1]+  Segmentation fault      ./test
[root@server myprogram]# 
想向进程发送SIGSEGV信号,用kill -SIGSEGV 1790的命令
为什么要enter两次它才输出segmentation fault呢?因为在进程1790结束之前已经已经回到了shell提示符,等待用户输入下一条命令,shell不希望段错误的信息和用户的输入交错在一起,所以等待用户输入命令以后才显示进程发生了段错误,并且生成一个core文件。
下面看一个示例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

void sigusr_handler(int signo)//用户信号1和用户信号2的处理函数
{
  switch(signo)
  {
    case SIGUSR1:
      printf("Parent : catch SIGUSR1\n");
      break;
    case SIGUSR2:
      printf("Child : catch SIGUSR2\n");
      break;
    default:
      printf("should not be here\n");
      break;
  }
  return ;
}

int main(void)
{
  pid_t ppid, cpid;

  if(signal(SIGUSR1, sigusr_handler) == SIG_ERR)
  {
    perror("can't set handler for SIGUSR1");
    exit(1);
  }

  if(signal(SIGUSR2, sigusr_handler) == SIG_ERR)
  {
    perror("can't set handler for SIGUSR2");
    exit(1);
  }

  ppid = getpid();//后去进程ID

  if((cpid = fork()) <0)//fork创建子进程
  {
    perror("fail to fork");
    exit(1);
  }
  else if(cpid == 0)//在子进程中
  {
    printf("child\n");
    if(kill(ppid, SIGUSR1) == -1)//往父进程发SIGUSR1信号
    {
      perror("fail to send signal");
      exit(1);
     }
     while(1)     ;
  }
  else//在父进程中
  {
    //printf("parent\n");
    sleep(1);
    if(kill(cpid, SIGUSR2) == -1)//往子进程中发SIGUSR2信号
    {
      perror("fail to send signal");
      exit(1);
    }

    printf("kill child\n");
    
    if(kill(cpid, SIGKILL) == -1)//kill子进程
    {
      perror("fail to send signal");
      exit(1);
    }

    if(wait(NULL) == -1)//等待一个子进程退出
    {
      perror("fail to wait");
      exit(1);
    }
  }

  return 0;
}
信号是有限的,我们不能随便定义自己的信号,有两个信号是留给用户自己用的,分别是SIGUSR1,SIGUSR2。
这个例子实现了发送信号,信号处理函数的应用
程序执行结果如下:
[root@server myprogram]# gcc -o test test.c
[root@server myprogram]# ./test
child
Parent : catch SIGUSR1
kill child
Child : catch SIGUSR2
有趣的是,如果我只是掉printf("child");这一句,那么Child : catch SIGUSR2这一句也打印不出来,等哪位知道原因的仁兄指教。。。