繁体   English   中英

从父母发送信号给孩子

[英]sending signal from parent to child

我正在使用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)被认为是不可靠和过时的。 这是一本很大的书,但值得投入时间。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM