一道关于信号的题

来源:互联网 发布:用网络看电视直播 编辑:程序博客网 时间:2024/04/30 10:34
信号是UNIX下常见的进程间通信机制。今天碰到一道信号方面题目,与大家分享,题目如下:
用fork创建两个子进程,调用signal()让父进程接收键盘上的中断信号(control-c),捕捉到信号后父进程用kill()向子进程发送自定义信号,子进程捕捉到信号后分别输出如下信息后终止:
[plain] view plain copy print?
  1. Child process 1 is killed by parent!  
  2. Child process 2 is killed by parent!  


父进程等待两个子进程结束后,输出如下信息后终止:
[plain] view plain copy print?
  1. Parent process is killed!  


现给出如下程序:
[cpp] view plain copy print?
  1. #include <stdlib.h>  
  2. #include <stdio.h>  
  3. #include <sys/wait.h>  
  4. #include <signal.h>  
  5. #include <unistd.h>  
  6.   
  7.   
  8. void waiting();  
  9. void stop();  
  10. static int wait_mark;  
  11.   
  12.   
  13. int main(int argc, const char *argv[])  
  14. {  
  15.     int p1, p2;  
  16.     int stdout;  
  17.     while ((p1 = fork()) == -1);  
  18.     if (p1 > 0)  
  19.     {  
  20.         while ((p2 = fork()) == -1);  
  21.         if (p2 > 0)  
  22.         {  
  23.             wait_mark = 1;  
  24.             signal(SIGINT, &stop);  
  25.             waiting();  
  26.             kill(p1, 16);  
  27.             wait(0);  
  28.             kill(p2, 17);  
  29.             wait(0);  
  30.             printf("Parent process is killed!\n");  
  31.             exit(0);  
  32.         }  
  33.         else  
  34.         {  
  35.             wait_mark = 1;  
  36.             signal(17, stop);  
  37.             waiting();  
  38.             lockf(stdout, 1, 0);  
  39.             printf("Child process 2 is killed by parent!\n");  
  40.             lockf(stdout, 0, 0);  
  41.             exit(0);  
  42.         }  
  43.     }  
  44.     else  
  45.     {  
  46.         wait_mark = 1;  
  47.         signal(16, stop);  
  48.         waiting();  
  49.         lockf(stdout, 1, 0);  
  50.         printf("Child process 1 is killed by parent!\n");  
  51.         lockf(stdout, 0, 0);  
  52.         exit(0);  
  53.     }  
  54.     return 0;  
  55. }  
  56.   
  57.   
  58. void waiting()  
  59. {  
  60.     while (wait_mark != 0);  
  61. }  
  62.   
  63.   
  64. void stop()  
  65. {  
  66.     wait_mark = 0;  
  67. }  

可以看到,应用程序运行后,在按下键盘control-c后显示
Parent process is killed!就返回了shell。为什么没有显示应该显示的另外两句呢?
通过调试发现,父进程在接收到control-c,发送消息给子进程让子进程结束的时候,子进程就已经结束掉了,原因是子进程也接收到了control-c的按键并且默认结束了自己,也就是子进程并不是因为父进程发送信号通知自己结束而结束的,而是自己接收到的SIGINT使得自己结束的。那么要得到正确的输出就很简单,可以这么做:
在子进程中把一个空函数注册为处理信号SIGINT的函数,这样子进程接收了到control-c还可以继续运行。
[cpp] view plain copy print?
  1. /* 定义一个空函数 */  
  2. void blank()  
  3. {  
  4.     /* do nothing */  
  5. }  

在子进程创建好后:
[cpp] view plain copy print?
  1. signal(SIGINT, blank);  

重新编译运行程序,就可以得到正确结果了。
[plain] view plain copy print?
  1. $ gcc a.c  
  2. $ ./a.out  
  3. ^CChild process 1 is killed by parent!  
  4. Child process 2 is killed by parent!  
  5. Parent process is killed!  

那么,假如不允许修改原来的程序,也要打印出正确结果怎么办呢?
方法也很简单,原来的程序之所以不能正确输出结果是因为两个子进程也接收到了control-c,因为这个按键是我们在shell里发送给程序的,解决的办法就是单独给父进程的发送SIGINT。
首先编译原来的程序:
[plain] view plain copy print?
  1. $ gcc b.c  

运行生成的可执行文件:
[plain] view plain copy print?
  1. $ ./a.out  

然后打开一个新的shell,找出所有的a.out。
[plain] view plain copy print?
  1. $ ps -e | grep 'a.out'  
  2.  3147 pts/0    00:00:01 a.out  
  3.  3148 pts/0    00:00:01 a.out  
  4.  3149 pts/0    00:00:01 a.out  

发现有三个a.out在运行,一般情况下数字小的就是父进程,也就是这里的3147,我们给这个进程单独发送SIGINT:
[plain] view plain copy print?
  1. $ kill -s INT 3147  

再切换回原来的shell,可以看到进程已经被终止了并且输出了正确结果:
[plain] view plain copy print?
  1. Child process 1 is killed by parent!  
  2. Child process 2 is killed by parent!  
  3. Parent process is killed! 
0 0
原创粉丝点击