信号是UNIX下常见的进程间通信机制。今天碰到一道信号方面题目,与大家分享,题目如下:
用fork创建两个子进程,调用signal()让父进程接收键盘上的中断信号(control-c),捕捉到信号后父进程用kill()向子进程发送自定义信号,子进程捕捉到信号后分别输出如下信息后终止:
[plain] view plain
copy
print ?
- Child process 1 is killed by parent!
- Child process 2 is killed by parent!
父进程等待两个子进程结束后,输出如下信息后终止:
[plain] view plain
copy
print ?
- Parent process is killed!
现给出如下程序:
[cpp] view plain
copy
print ?
- #include <stdlib.h>
- #include <stdio.h>
- #include <sys/wait.h>
- #include <signal.h>
- #include <unistd.h>
-
-
- void waiting();
- void stop();
- static int wait_mark;
-
-
- int main(int argc, const char *argv[])
- {
- int p1, p2;
- int stdout;
- while ((p1 = fork()) == -1);
- if (p1 > 0)
- {
- while ((p2 = fork()) == -1);
- if (p2 > 0)
- {
- wait_mark = 1;
- signal(SIGINT, &stop);
- waiting();
- kill(p1, 16);
- wait(0);
- kill(p2, 17);
- wait(0);
- printf("Parent process is killed!\n");
- exit(0);
- }
- else
- {
- wait_mark = 1;
- signal(17, stop);
- waiting();
- lockf(stdout, 1, 0);
- printf("Child process 2 is killed by parent!\n");
- lockf(stdout, 0, 0);
- exit(0);
- }
- }
- else
- {
- wait_mark = 1;
- signal(16, stop);
- waiting();
- lockf(stdout, 1, 0);
- printf("Child process 1 is killed by parent!\n");
- lockf(stdout, 0, 0);
- exit(0);
- }
- return 0;
- }
-
-
- void waiting()
- {
- while (wait_mark != 0);
- }
-
-
- void stop()
- {
- wait_mark = 0;
- }
可以看到,应用程序运行后,在按下键盘control-c后显示
Parent process is killed!就返回了shell。为什么没有显示应该显示的另外两句呢?
通过调试发现,父进程在接收到control-c,发送消息给子进程让子进程结束的时候,子进程就已经结束掉了,原因是子进程也接收到了control-c的按键并且默认结束了自己,也就是子进程并不是因为父进程发送信号通知自己结束而结束的,而是自己接收到的SIGINT使得自己结束的。那么要得到正确的输出就很简单,可以这么做:
在子进程中把一个空函数注册为处理信号SIGINT的函数,这样子进程接收了到control-c还可以继续运行。
[cpp] view plain
copy
print ?
-
- void blank()
- {
-
- }
在子进程创建好后:
[cpp] view plain
copy
print ?
- signal(SIGINT, blank);
重新编译运行程序,就可以得到正确结果了。
[plain] view plain
copy
print ?
- $ gcc a.c
- $ ./a.out
- ^CChild process 1 is killed by parent!
- Child process 2 is killed by parent!
- Parent process is killed!
那么,假如不允许修改原来的程序,也要打印出正确结果怎么办呢?
方法也很简单,原来的程序之所以不能正确输出结果是因为两个子进程也接收到了control-c,因为这个按键是我们在shell里发送给程序的,解决的办法就是单独给父进程的发送SIGINT。
首先编译原来的程序:
[plain] view plain
copy
print ?
- $ gcc b.c
运行生成的可执行文件:
[plain] view plain
copy
print ?
- $ ./a.out
然后打开一个新的shell,找出所有的a.out。
[plain] view plain
copy
print ?
- $ ps -e | grep 'a.out'
- 3147 pts/0 00:00:01 a.out
- 3148 pts/0 00:00:01 a.out
- 3149 pts/0 00:00:01 a.out
发现有三个a.out在运行,一般情况下数字小的就是父进程,也就是这里的3147,我们给这个进程单独发送SIGINT:
[plain] view plain
copy
print ?
- $ kill -s INT 3147
再切换回原来的shell,可以看到进程已经被终止了并且输出了正确结果:
[plain] view plain
copy
print ?
- Child process 1 is killed by parent!
- Child process 2 is killed by parent!
- Parent process is killed!