Linux信号:孩子没有收到

Posted

技术标签:

【中文标题】Linux信号:孩子没有收到【英文标题】:Linux Signal : Child not receiving 【发布时间】:2022-01-22 00:18:22 【问题描述】:

我的代码是一个简单的 c 代码,其中有两个进程,第一个是父母发送信号并打印出它发送的内容,另一个是接收信号并打印它收到的内容的孩子 我的代码的输出是 家长:发送 SIGHUP 父母:发送信号 家长:发送 SIGQUIT 它应该是 家长:发送 SIGHUP 孩子收到 SIGHUP 等等……

 // C program to implement sighup(), sigint()
    // and sigquit() signal functions
    #include <signal.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <unistd.h>

    
    // function declaration
    void sighup(int);
    void sigint(int);
    void sigquit(int );
    void sigsegv(int );
    
    // driver code
    int main()
    
      int pid;
    
      /* get child process */
      if ((pid = fork()) < 0) 
        perror("fork");
        exit(1);
      
    
      if (pid == 0)  /* child */
        signal(SIGHUP, sighup);
        signal(SIGINT, sigint);
        signal(SIGQUIT, sigquit);
                    signal(SIGSEGV, sigsegv);
        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);
      
return  0 ;
    
    
    // sighup() function definition
    void sighup(int signo)
    
    
      signal(SIGHUP, sighup); /* reset signal */
      printf("CHILD: 1 [sighub]\n");
    
    
    // sigint() function definition
    void sigint(int signo)
    
    
      signal(SIGINT, sigint); /* reset signal */
      printf("CHILD: 2 [sigint]\n");
    
    // sigsegv() function definition
    void sigsegv(int signo)
    
    
      signal(SIGSEGV, sigsegv); /* reset signal */
      printf("CHILD: 11 [sigsegv]\n");
    
    
    // sigquit() function definition
    void sigquit(int signo)
    
    signal(SIGINT, sigquit); /* reset signal */
      printf("3 [sigquit]\n");
      
    

【问题讨论】:

这段代码中的一些习语已经过时和/或危险了。请don't call printf in a signal handler,而只使用async-signal-safe 函数。也请use sigaction, not signal。至少,您不需要遵循旧的 SysV 约定重新安装信号处理程序。 【参考方案1】:

您的代码逻辑有几个“问题”(除了使用非异步信号安全函数)。

    无法保证在fork() 之后哪个进程将开始工作。这取决于内核和系统。 Linux 内核允许您通过在/proc/sys/kernel/sched_child_runs_first 中设置一个值来使其具有确定性,其中如果值为 0(默认),则父级首先运行,如果值为非零,则子级首先运行。然而,依赖此开关进行同步并不是一个好习惯,因为在多处理器环境中,如果我们有可用的 CPU,两者可以同时运行。

    进程可以在任何给定时刻被抢占,无论是由于系统调用的使用,还是由于 cpu 时间。因此,不能保证孩子在父母发出信号之前已经设置了所有的信号处理程序,所以一些信号丢失了(实际上在我的测试中,我遇到了所有信号丢失的情况,一旦睡眠被移除)。

    和2一样,不能保证在parent发送新信号之前child得到信号。

为了解决问题并保证执行顺序,您需要创建一个同步屏障。这是基于确保同步的 IPC 管道的示例。在示例中,父母等待孩子设置信号,然后,对于它发送的每个信号,它都等待孩子确认信号已收到。

// C program to implement sighup(), sigint()
// and sigquit() signal functions
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>


// function declaration
void sighup(int);
void sigint(int);
void sigquit(int );
void sigsegv(int );

int fd[2]; // communication pipe

// driver code
int main()

    int pid;

    if (pipe(fd)==-1)
    
        fprintf(stderr, "Pipe Failed" );
        exit(1);
    

    /* get child process */
    if ((pid = fork()) < 0) 
        perror("fork");
        exit(1);
    

    if (pid == 0)  /* child */
        close(fd[0]); // Close reading end
        printf("Preparing the signals");
        signal(SIGHUP, sighup);
        printf(".");
        signal(SIGINT, sigint);
        printf(".");
        signal(SIGQUIT, sigquit);
        printf(".");
        signal(SIGSEGV, sigsegv);
        printf(".Signals ready\n");
        write(fd[1],"ready\0", 6);
        for (;;)
            ; /* loop for ever */
    

    else /* parent */
     /* pid hold id of child */
        char readb[1024];
        readb[1023] = 0; //Safty stop

        close(fd[1]); // Close writting end
        printf("Waiting for child to finish\n");
        read(fd[0], readb, 1023);
        printf("Child sends %s", readb);

        printf("\nPARENT: sending SIGHUP\n\n");
        kill(pid, SIGHUP);
        read(fd[0], readb, 1023);
        printf("%s\n", readb);

        printf("\nPARENT: sending SIGINT\n\n");
        kill(pid, SIGINT);
        read(fd[0], readb, 1023);
        printf("%s\n", readb);

        printf("\nPARENT: sending SIGQUIT\n\n");
        kill(pid, SIGQUIT);
        read(fd[0], readb, 1023);
        printf("%s\n", readb);

        close(fd[0]);
    
    return  0 ;


// sighup() function definition
void sighup(int signo)

    signal(SIGHUP, sighup); /* reset signal */
    write(fd[1],"CHILD: 1 [sighub]\0",18);


// sigint() function definition
void sigint(int signo)


    signal(SIGINT, sigint); /* reset signal */
    write(fd[1],"CHILD: 2 [sigint]\0",18);

// sigsegv() function definition
void sigsegv(int signo)

    signal(SIGSEGV, sigsegv); /* reset signal */
    write(fd[1],"CHILD: 11 [sigsegv]\0",20);


// sigquit() function definition
void sigquit(int signo)

    signal(SIGINT, sigquit); /* reset signal */
    write(fd[1],"CHILD: 3 [sigquit]\0",19);

【讨论】:

【参考方案2】:

第一个信号可能是在孩子初始化它的信号处理之前发送的。至少我可以通过在if (pid == 0) 之后直接添加sleep(2); 来重现该行为。

在开始发送信号之前尝试在父进程中添加sleep()

【讨论】:

以上是关于Linux信号:孩子没有收到的主要内容,如果未能解决你的问题,请参考以下文章

Linux信号处理

Linux_信号与信号量

linux信号-------初涉

Linux操作系统进程信号

linux 多线程信号处理总结

Linux 进程间通信 --信号