2.2 linux中的信号分析

信号:

  信号是UNIX系统响应某些状况而产生的事件,进程在接收到信号时会采取相应的行动。

  信号是因为某些错误条件而产生的,比如内存段冲突、浮点处理器错误或者非法指令等。

  信号是在软件层次上对中断的一种模拟,所以通常把它称为是软中断。

  信号和中断的区别:  

    相似点:

      采用了相同的异步通信方式。

      当检测出有信号或者中断请求时,都暂停正在执行的程序而转去执行相应的处理程序。

      都在处理完毕后返回到原来的断点。

      对信号和中断都可以进行屏蔽。

    区别:

      中断有优先级,而信号没有优先级,所有的信号都是平等的。

      信号处理程序都是在用户态下运行的,而中断处理程序是在核心态下运行。

      中断响应是及时的,而信号响应通常都有较大的延迟。

  常用的信号如下所示:

man 7 signal可以查看信号的默认动作和信号的含义。

  signal用于安装一个信号处理函数,原型如下:

  __sighandler_t  signal(int signum, __sighandler_t  handler)

  signal是一个带signum和handler两个参数的函数,准备捕捉或者屏蔽的信号由参数signum给出,接收到指定信号时将要调用的函数由handler给出。

  handler这个函数必须有一个int型参数(即接收到的信号代码),它本身的类型是void。

  handler也可以是下面两个特殊值:

    SIG_IGN  忽略该信号

    SIG_DFL  恢复默认行为

示例程序如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <signal.h>
 4 
 5 
 6 void handler(int num)
 7 {
 8     printf("recv signal num = %d\n", num);
 9 }
10 
11 
12 int main()
13 {
14     pid_t pid;
15     
16     signal(SIGCHLD, SIG_IGN);
17     
18     pid = fork();
19     
20     if(pid == -1)
21     {
22         perror("fork error");
23         exit(0);
24     }
25     
26     if(pid == 0)
27     {
28         printf("child ... \n");
29         exit(0);
30     }
31     while(1)
32     {
33         pause();    
34     }
35     
36     return 0;
37 }

程序中,我们注册信号时,表示父进程忽略子进程的退出信号,因此,执行结果如下:

默认情况下(当我们不使用signal信号时),父进程会在退出的时候给子进程收尸(前提是子进程先死),可用如下程序证明:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <signal.h>
 5 
 6 
 7 void handler(int num)
 8 {
 9     printf("recv signal num = %d\n", num);
10 }
11 
12 
13 int main()
14 {
15     pid_t pid;
16     
17     //signal(SIGCHLD, SIG_IGN);
18     
19     pid = fork();
20     
21     if(pid == -1)
22     {
23         perror("fork error");
24         exit(0);
25     }
26     
27     if(pid == 0)
28     {
29         printf("child ... \n");
30         exit(0);
31     }
32     
33     sleep(5);
34     //while(1)
35     //{
36     //    pause();    
37     //}
38     
39     return 0;
40 }

我们让父进程睡眠5秒,保证子进程先死,当父进程还在睡眠时,我们在另一个中断执行ps -ef,结果如下:

可以看到,父进程睡眠期间,子进程已经死了,而且处于僵尸状态,但是当父进程也结束时,子进程的僵尸状态消失了,说明父进程给它收尸了。当把17行的注释打开时,就是告诉内核,子进程死的时候让内核给他收尸,因此,我们看不到子进程处于僵尸状态的现象了(内核收尸太快了)。

  下面我们演示注册一个真正的信号处理函数和恢复默认行为的程序:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <signal.h>
 5 
 6 
 7 void handler(int num)
 8 {
 9     printf("recv signal num = %d\n", num);
10 }
11 
12 
13 int main()
14 {
15     pid_t pid;
16     char c;
17     
18     signal(SIGINT, handler);
19     
20     while( (c = getchar()) != 'a')
21     {
22         pause();
23     }
24     
25     signal(SIGINT, SIG_DFL);
26     while(1)
27     {
28         pause();    
29     }
30     
31     return 0;
32 }

SIGINT代表ctrl+c信号,18行将这个信号的处理函数注册为handler,当程序停在20行时,我们按下ctrl+c,handler会得到执行。 输入a使程序执行到26行,这时候SIGINT信号的行为恢复到了默认行为(25行中的SIG_DFL表示恢复默认行为),我们再按下ctrl+c,程序直接退出了(这就是默认行为)。执行现象如下:

   signal函数执行成功时,返回默认的处理函数,执行失败时返回SIG_ERR。我们将上述程序改成以下方式:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <signal.h>
 5 
 6 void handler(int num)
 7 {
 8     printf("recv signal num = %d\n", num);
 9 }
10 
11 int main()
12 {
13     pid_t pid;
14     char c;
15     
16     __sighandler_t old = signal(SIGINT, handler);
17     
18     if(SIG_ERR == old)
19     {
20         perror("signal error");
21     }
22     
23     while( (c = getchar()) != 'a')
24     {
25         pause();
26     }
27     
28     signal(SIGINT, old);
29     printf("huan yuan\n");
30     while(1)
31     {
32         pause();    
33     }
34     
35     return 0;
36 }

使用signal注册信号处理函数时,将默认的处理函数返回到old中,并在下面进行了错误处理,28行恢复默认行为,执行结果如下:

 信号的分类:可靠与不可靠信号,实时与非实时信号。实时信号都是可靠信号,非实时信号都是不可靠信号。1-31号都是不可靠信号。不可靠信号就是向应用程序发送了多次信号,应用程序可能只接收到了一次。可靠信号就是向应用程序发几次信号都能保证全接收到。早期unix系统每接收到一个信号就将处理程序恢复到默认行为。现在的linux中的不可靠信号主要指信号可能会丢失。

猜你喜欢

转载自www.cnblogs.com/wanmeishenghuo/p/9369048.html
2.2
今日推荐