从父母发送信号到孩子

我从网站http://www.code2learn.com/2011/01/signal-program-using-parent-child.html使用这个教程,试图理解为什么信号不被孩子接收?

这里是代码:

#include <stdio.h> #include <signal.h> #include <stdlib.h> void sighup(); /* routines child will call upon sigtrap */ void sigint(); void sigquit(); void main() { int pid; /* get child process */ if ((pid = fork()) < 0) { perror("fork"); exit(1); } if (pid == 0) { /* child */ signal(SIGHUP,sighup); /* set function calls */ signal(SIGINT,sigint); signal(SIGQUIT, sigquit); for(;;); /* loop for ever */ } else /* parent */ { /* pid hold id of child */ printf("\nPARENT: sending SIGHUP\n\n"); kill(pid,SIGHUP); sleep(3); /* pause for 3 secs */ printf("\nPARENT: sending SIGINT\n\n"); kill(pid,SIGINT); sleep(3); /* pause for 3 secs */ printf("\nPARENT: sending SIGQUIT\n\n"); kill(pid,SIGQUIT); sleep(3); } } void sighup() { signal(SIGHUP,sighup); /* reset signal */ printf("CHILD: I have received a SIGHUP\n"); } void sigint() { signal(SIGINT,sigint); /* reset signal */ printf("CHILD: I have received a SIGINT\n"); } void sigquit() { printf("My DADDY has Killed me!!!\n"); exit(0); } 

输出:

在这里输入图像描述

这是一个竞争条件。 你的代码假设孩子先运行,并且不被父母抢占,直到它安装了所有的信号处理程序并开始循环。

如果情况并非如此,那么在孩子有机会接收信号之前,家长可能会向孩子发出一个信号。 因此,子进程被终止,因为SIGHUPSIGINTSIGQUIT的默认行为是终止的。

在你的具体情况下,你从来没有看到任何从孩子的输出。 这意味着父母发送了SIGHUP给孩子,并且SIGHUP在孩子改变了默认行为之前被传递了。 所以这个小孩被杀了

实际上,如果你对kill(2)的返回值进行了一些错误检查 – 你应该这样做 – 你会在试图发送SIGINTSIGQUIT看到ESRCH ,因为这个孩子已经离开了(假设没有其他进程系统启动,同时赋予相同的PID)。

那么,你如何解决它? 要么使用某种形式的同步来强制孩子先运行,并且只有在所有信号处理程序安装完毕后才能执行父代,或者分叉之前设置信号处理程序,然后在父代中取消设置。 下面的代码使用后一种方法:

 #include <stdio.h> #include <signal.h> #include <stdlib.h> #include <unistd.h> void sighup(int); /* routines child will call upon sigtrap */ void sigint(int); void sigquit(int); int main(void) { int pid; signal(SIGHUP,sighup); /* set function calls */ signal(SIGINT,sigint); signal(SIGQUIT, sigquit); /* get child process */ if ((pid = fork()) < 0) { perror("fork"); exit(1); } if (pid == 0) { /* child */ for(;;); /* loop for ever */ } else { signal(SIGHUP, SIG_DFL); signal(SIGINT, SIG_DFL); signal(SIGQUIT, SIG_DFL); /* parent */ /* pid hold id of child */ printf("\nPARENT: sending SIGHUP\n\n"); kill(pid,SIGHUP); sleep(3); /* pause for 3 secs */ printf("\nPARENT: sending SIGINT\n\n"); kill(pid,SIGINT); sleep(3); /* pause for 3 secs */ printf("\nPARENT: sending SIGQUIT\n\n"); kill(pid,SIGQUIT); sleep(3); } return 0; } void sighup(int signo) { signal(SIGHUP,sighup); /* reset signal */ printf("CHILD: I have received a SIGHUP\n"); } void sigint(int signo) { signal(SIGINT,sigint); /* reset signal */ printf("CHILD: I have received a SIGINT\n"); } void sigquit(int signo) { printf("My DADDY has Killed me!!!\n"); exit(0); } 

另外,你不应该使用signal(2) :它在许多方面是不可靠的,它的确切语义是平台相关的。 为了确保最大的可移植性,您应该使用sigaction(2) 。 请参阅手册以了解更多信息。 下面是使用sigaction(2)代替的代码:

 #include <stdio.h> #include <signal.h> #include <stdlib.h> #include <unistd.h> void sighup(int); /* routines child will call upon sigtrap */ void sigint(int); void sigquit(int); int main(void) { struct sigaction sigact; sigact.sa_flags = 0; sigemptyset(&sigact.sa_mask); sigact.sa_handler = sighup; if (sigaction(SIGHUP, &sigact, NULL) < 0) { perror("sigaction()"); exit(1); } sigact.sa_handler = sigint; if (sigaction(SIGINT, &sigact, NULL) < 0) { perror("sigaction()"); exit(1); } sigact.sa_handler = sigquit; if (sigaction(SIGQUIT, &sigact, NULL) < 0) { perror("sigaction()"); exit(1); } pid_t pid; /* get child process */ if ((pid = fork()) < 0) { perror("fork"); exit(1); } if (pid == 0) { /* child */ for(;;); /* loop for ever */ } else { sigact.sa_handler = SIG_DFL; sigaction(SIGHUP, &sigact, NULL); sigaction(SIGINT, &sigact, NULL); sigaction(SIGQUIT, &sigact, NULL); /* parent */ /* pid hold id of child */ printf("\nPARENT: sending SIGHUP\n\n"); kill(pid,SIGHUP); sleep(3); /* pause for 3 secs */ printf("\nPARENT: sending SIGINT\n\n"); kill(pid,SIGINT); sleep(3); /* pause for 3 secs */ printf("\nPARENT: sending SIGQUIT\n\n"); kill(pid,SIGQUIT); sleep(3); } return 0; } void sighup(int signo) { signal(SIGHUP,sighup); /* reset signal */ printf("CHILD: I have received a SIGHUP\n"); } void sigint(int signo) { signal(SIGINT,sigint); /* reset signal */ printf("CHILD: I have received a SIGINT\n"); } void sigquit(int signo) { printf("My DADDY has Killed me!!!\n"); exit(0); } 

最后但并非最不重要的一点是,必须提及的是,应该始终使用-Wall编译。 你的程序有一些错误:

  • main()的返回类型应该是int
  • 信号处理程序接收信号编号作为参数,请使用正确的原型和声明。
  • fork(2)返回一个pid_t ,而不是一个int ,请使用正确的类型。
  • 您需要包含unistd.h以获取fork(2)的正确原型。
  • printf(3)不是异步信号安全的,因此您不应该在信号处理程序中调用它。 在这个玩具程序中可以看到信号是如何协同工作的,但请记住,你绝对不应该在现实世界中做到这一点。 要查看异步信号安全功能列表以及每个信号的默认操作,请参阅man 7 signal

建议的话:停止从该网站学习。 如果你想学习这种东西,请阅读UNIX环境中的高级编程 。 直接去了解10为什么signal(2)被认为是不可靠和过时的。 这是一本很大的书,但是值得投入你的时间。