在信号处理程序本身内部等待信号

时间:2019-03-10 09:35:09

标签: c unix signals posix ipc

我试图编写一个程序,该程序涉及将信号发送到一个过程,以通知它暂停一段时间,然后在收到另一个信号后重新开始工作。我写了这样的信号处理程序:

void sig_handler(int alrm)
{
    if(sig_rcv==0)
    {
        printf("Signal received..sig_rcv value: %d\n",sig_rcv);
        sig_rcv = (sig_rcv+1)%2;
        printf("After increment sig_rcv value: %d\n",sig_rcv);
        signal(SIGUSR1,sig_handler);
        if(pause()<0)
        {
            printf("Signal received again..\n");
        }
    }
    else
    {
        sig_rcv = (sig_rcv+1)%2;
        printf("Signal received..sig_rcv value: %d\n",sig_rcv);
        signal(SIGUSR1,sig_handler);
    }
    printf("Exiting..\n");
}

这里我要维护一个初始值为0的全局变量sig_rcv,如果在零时收到信号,它将进入if条件并暂停另一个信号;另一方面,如果它在sig_rcv为1,它将仅更改该变量的值。 我以这种方式编写信号处理程序的目的是将相同的信号用于两个不同的目的。我使用了这个系统调用:

signal(SIGUSR1,sig_handler);

现在,当我将SIGUSR1发送到该进程时,它正在执行直至pause()语句。但是在第二次发送SIGUSR1之后,它没有显示任何效果,只是冻结在pause语句上。在我使用另一个信号删除暂停状态后,此问题已解决,例如:

void sig_handler2(int a)
{
    sig_rcv = (sig_rcv+1)%2;
    printf("Restarting reading...\n");
}

signal(SIGUSR2,sig_handler2);

在这种情况下,它工作得很好。 因此,我的问题是,为什么会发生这种现象?我们不能在执行为同一信号编写的信号处理程序时等待信号吗?如果是这样,原因是什么?不用SIGUSR2,有什么方法可以达到相同的结果吗?

3 个答案:

答案 0 :(得分:5)

  

我们不能在执行为同一信号编写的信号处理程序时等待信号吗?

代码是“永远”等待还是已经处理后再次接收到相同信号,取决于(C / OS)实现。

来自POSIX documentation

  在信号的产生与传递或接受之间的时间内,该信号被称为“待定”。

  

如果产生了随后发生的待处理信号,则由实施定义来确定信号是被传送还是被接收多次[...]


而且,顺便说一句,calling printf() & friends from a signal handler is unsafe

答案 1 :(得分:2)

比起@alk来说,更普遍的是,您不能在严格符合C代码的信号处理程序中调用任何库函数。

7.1.4 Use of library functions, paragraph 4 of the C standard

  

不保证标准库中的函数是可重入的,并且可能会在静态或线程存储期间修改对象。188

对于该段,注意footnote 188指出:

  

因此,信号处理程序通常无法调用标准库函数。

如@alk所述,POSIX允许您仅从信号处理程序中安全地调用异步信号安全函数。

缺少平台的特定文档,该文档指出可以从信号处理程序中安全地进行特定的函数调用,因此不能从信号处理程序中进行功能调用。

答案 2 :(得分:2)

由于您使用的是signal而不是sigaction,因此请务必注意the POSIX standard中的以下内容:

  

当信号出现并且 func 指向一个函数时,是否等同于a:

由实现定义      

signal(sig, SIG_DFL);

     执行

或实现阻止某些实现定义的信号集(至少包括 sig )的出现,直到完成当前信号处理为止。

这反映了两个历史性的信号实现,即SVID和BSD。 由于您使用的是Ubuntu 18.04,因此很可能使用glibc,它实现了后者(BSD)的语义。 SIGUSR1在其处理程序执行时被屏蔽。

由于您需要SVID语义,即不会屏蔽任何信号,并且每次调用信号处理程序时都需要重新建立信号处理程序,因此应将signal(SIGUSR1, sig_handler);调用替换为以下内容:

struct sigaction sa = { .sa_handler = sig_handler, .sa_flags = SA_NODEFER|SA_RESETHAND };
sigemptyset(&sa.mask);
sigaction(SIGUSR1, &sa, NULL);

SA_NODEFER标志与空掩码一起表示没有信号将被掩码; SA_RESETHAND表示信号操作将重置为SIG_DFL

此外,如其他答案所述,您不应从信号处理程序内部调用printf。 Linux signal safety手册页说明了可以调用的功能。 sigactionsignalpause正常。您可以使用write而不是printf来写字符串。