【问题标题】:Get if a signal is received获取是否收到信号
【发布时间】:2024-01-10 09:42:01
【问题描述】:

如果进程接收到信号,我如何获得?以下示例的目的是 fork 一个进程,用子进程读取一个字符并将 SIGUSR1 发送给父进程,但如果 10 秒后用户仍然需要插入该字符,则子进程终止。问题是如何知道是否收到了 SIGUSR1:

#define _POSIX_SOURCE

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>

void handle_USR1()
{
    return;
}

int main(void)
{
    int p[2];
    pid_t pid;

    if(pipe(p) == -1)
    {
        perror("pipe");
        return -1;
    }

    if((pid = fork()) == -1)
    {
        perror("fork");
        return -1;
    }

    if(pid == 0)
    {
        close(p[0]);
        char ch, b;

        ch = getchar();

        if(ch != '\n')
            while((b = getchar()) != '\n' && b != EOF);

        write(p[1], &ch, 1);
        kill(getppid(),SIGUSR1);

        exit(-1);
    }
    else
    {
        signal(SIGUSR1,handle_USR1);
        close(p[1]);
        char ch;

        sleep(10);
        kill(pid, SIGTERM);

        read(p[0],&ch,1);

        //if(/*SIGUSR1 is not recived*/)
        //{
        //    exit(-1);
        //}

        printf("\n\n%c",ch);
    }

    return 0;
}

我可以用什么替换/*SIGUSR1 is not recived*/

【问题讨论】:

  • 您的handle_USR1() 函数应该将static volatile sig_atomic_t 变量设置为已知值(例如1)。然后,您可以检查该变量以了解是否已收到信号。在标准 C 中,您可以在信号处理程序中以严格一致的方式执行其他操作。在 POSIX 中,您可能会做得更多(但要小心:Avoid using printf() in a signal handler 可能会帮助您。
  • 好的,所以没有标准 C 函数可以做到这一点,对吧?
  • @SimoneBonato 不,但为什么要有一个? Jonathan Leffler 的方法非常好,而且事情通常都是这样完成的。

标签: c signals


【解决方案1】:

comment 中,我建议:

您的handle_USR1() 函数应将static volatile sig_atomic_t 变量设置为已知值(例如1)。然后,您可以检查该变量以了解是否已收到信号。在标准 C 中,您可以在信号处理程序中以严格一致的方式执行其他操作。在 POSIX 中,您可能会做得更多(但要小心:Avoid using printf() in a signal handler 可能会帮助您)。

我的意思是根据您的代码翻译成代码:

sigusr1.c

#define _POSIX_SOURCE

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>

static volatile sig_atomic_t sig = 0;

static void handle_USR1(int signum)
{
    sig = signum;
}

int main(void)
{
    int p[2];
    pid_t pid;

    signal(SIGUSR1, handle_USR1);

    if (pipe(p) == -1)
    {
        perror("pipe");
        return -1;
    }

    if ((pid = fork()) == -1)
    {
        perror("fork");
        return -1;
    }

    if (pid == 0)
    {
        close(p[0]);
        char ch, b;

        ch = getchar();

        if (ch != '\n')
            while ((b = getchar()) != '\n' && b != EOF)
                ;

        write(p[1], &ch, 1);
        kill(getppid(), SIGUSR1);

        exit(-1);
    }
    else
    {
        close(p[1]);
        char ch;

        sleep(10);
        if (sig != 0)
            printf("Signal number %d received while sleeping\n", sig);
        else
        {
            printf("Sending SIGTERM to child %d\n", (int)pid);
            kill(pid, SIGTERM);
        }

        if (read(p[0], &ch, 1) == 1)
            printf("Character sent by child: [%c]\n", ch);
        else
            printf("No data sent by child before it died\n");
    }

    return 0;
}

编译

Mac OS X 10.11.3 El Capitan 上的 GCC 5.3.0:

$ gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
>     -Wold-style-definition -Werror sigusr1.c -o sigusr1
$

示例输出

$ ./sigusr1
Sending SIGTERM to child 70657
No data sent by child before it died
$ ./sigusr1
Kaput
Signal number 30 received while sleeping
Character sent by child: [K]
$

【讨论】:

  • 您可能会注意到,如果孩子没有获得数据(例如,因为用户键入 Control-D,或者标准输入是从 /dev/null 重定向的),那么孩子不会正确处理该数据并且向父级发送 0xFF。我不会解决这个问题——但谨慎总是合适的。
【解决方案2】:

请查看man 2 signal 提供以下概要

#include <signal.h>

   typedef void (*sighandler_t)(int);

   sighandler_t signal(int signum, sighandler_t handler);

你使用signal注册你的handler,handler的类型是

 void handler(int signum) {
     //do whatever you like
 }

这里的参数sugnum 接收信号编号。在您的情况下,它是SIGUSR1,并且可以在句柄内确定它是哪个信号

【讨论】:

  • 请重新阅读问题。这实际上不是所要求的。