从父母发送信号给孩子

时间:2015-06-28 07:04:43

标签: c linux signals fork 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);  
}  

输出:

enter image description here

1 个答案:

答案 0 :(得分:9)

这是一场竞争条件。您的代码假定子进程首先运行并且不会被父进程抢占,直到它安装所有信号处理程序并开始永远循环。

如果不是这种情况,父母可能会在孩子有机会接收信号之前向孩子发出信号。因此,子进程被终止,因为SIGHUPSIGINTSIGQUIT的默认操作将终止。

在您的具体情况下,您永远不会看到孩子的任何输出。这意味着父级已向子级发送SIGHUP,并且在子级更改默认行为之前已传递SIGHUP。所以孩子被杀了。

实际上,如果您对kill(2)的返回值进行了一些错误检查 - 您应该这样做 - 在尝试发送ESRCH和{{1时,您会在父级中看到SIGINT因为孩子已经离开了(假设系统中没有其他进程被启动并且在此期间被分配了相同的PID)。

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

SIGQUIT

另外,你不应该使用#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)

建议:停止从该网站学习。如果您想学习这类内容,请阅读UNIX环境中的高级编程。直接进入第10章,了解为什么man 7 signal被认为是不可靠和过时的。这本书很重要,但值得投入时间。