use*_*878 6 c linux fork signals ipc
我正在使用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);
}
Run Code Online (Sandbox Code Playgroud)
输出:

Fil*_*ves 10
这是竞争条件.您的代码假定子进程先运行并且不会被父进程抢占,直到它安装所有信号处理程序并开始永远循环.
如果不是这种情况,父母可能会在孩子有机会接收信号之前向孩子发出信号.因此,子进程被终止,因为默认操作为SIGHUP,SIGINT并且SIGQUIT将终止.
在您的具体情况下,您永远不会看到孩子的任何输出.这意味着父级发送SIGHUP给子级,并SIGHUP在子级更改默认行为之前传递.所以孩子被杀了.
实际上,如果你对返回值进行了一些错误检查kill(2)- 你应该 - 你会ESRCH在尝试发送时在父进程中看到,SIGINT并且SIGQUIT因为孩子已经离开(假设系统中没有其他进程被启动并被分配了在此期间同样的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);
}
Run Code Online (Sandbox Code Playgroud)
此外,您不应该使用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);
}
Run Code Online (Sandbox Code Playgroud)
最后,但并非最不重要的是,重要的是要提到你应该总是编译-Wall.你的程序有一些错误:
main()应该是int.fork(2)返回a pid_t,而不是int,请使用正确的类型.unistd.h以获得正确的原型fork(2).printf(3)不是异步信号安全的,因此你不应该在信号处理程序中调用它.在这个玩具程序中可以看到信号如何协同工作,但请记住,你不应该在现实世界中这样做.要查看异步信号安全功能列表以及每个信号的默认操作,请参阅man 7 signal.建议:停止从该网站学习.如果您想学习这类内容,请阅读UNIX环境中的高级编程.直接进入第10章,了解为什么确切地signal(2)认为不可靠和过时.这是一本很大的书,但值得投入时间.