再次调用信号处理程序

时间:2018-03-28 11:09:06

标签: c linux signals

我看到一个代码说明如何进行安全的信号处理。我不明白为什么信号处理程序再次调用signal (sig, catch_alarm);。这样做的原因是什么?没有它,代码也可以工作。

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


/* This flag controls termination of the main loop. */
volatile sig_atomic_t keep_going = 1;

/* The signal handler just clears the flag and re-enables itself. */
void
catch_alarm (int sig)
{
    keep_going = 0;
    signal (sig, catch_alarm);   //  <----- ???
}

void
do_stuff (void)
{
    puts ("Doing stuff while waiting for alarm....");
}

int
main (void)
{
    /* Establish a handler for SIGALRM signals. */
    signal (SIGALRM, catch_alarm);

    /* Set an alarm to go off in a little while. */
    alarm (2);

    /* Check the flag once in a while to see when to quit. */
    while (keep_going)
        do_stuff ();

    return EXIT_SUCCESS;
}

3 个答案:

答案 0 :(得分:4)

问题在于signal()的行为在UNIX版本中各不相同,并且在不同版本的Linux上也有所不同(引自Linux man)。特别是:

  

在原始UNIX系统中,使用时建立的处理程序          signal()是通过传递信号来调用的          信号将重置为SIG_DFL,系统没有阻塞          提供进一步的信号实例。这相当于          使用以下标志调用sigaction(2):

     

sa.sa_flags = SA_RESETHAND | SA_NODEFER;

因此,在这样的系统中,您必须在信号传递后再次呼叫signal()。由于这些可移植性问题,手册页以:

开头
  

signal()的行为在UNIX版本中各不相同,并且在不同版本的Linux上也有不同的历史变化。避免使用:         改为使用sigaction(2)。请参阅下面的便携性。

答案 1 :(得分:1)

简单地说:你错了,代码错了。

或者,您假设signal调用是多余的 - 它不存在,并且存在使代码在使用其他允许的signal语义的平台上正常运行。

此外,摘录不是关于 signal功能的安全信号处理; 它是关于如何通过更改类型volatile sig_atomic_t的变量从信号处理程序传递一个事件 - 只有一个可移植的方式 - 。要立即编写安全的可移植代码,您可以使用函数sigaction

Linux signal(2) manuals say

  

signal()的行为因UNIX版本而异,也有          不同版本的Linux在历史上各不相同。避免它          使用:改为使用sigaction(2)

  

signal()的唯一便携式用途是将信号的处置设置为SIG_DFLSIG_IGN。          使用signal()建立信号处理程序时的语义因系统而异(和          POSIX.1明确允许这种变化); 请勿将其用于此目的。

     

POSIX.1通过指定sigaction(2)解决了可移植性问题          在信号处理程序时提供语义的显式控制          调用;使用该界面而不是signal()

  

Linux的情况如下:

     
      
  • 内核的signal()系统调用提供了System V语义。
  •   
  • 默认情况下,在glibc 2及更高版本中,signal()包装函数不会调用内核        系统调用。相反,它使用提供BSD语义的标志来调用sigaction(2)。这个        只要定义了合适的功能测试宏,就会提供默认行为:        glibc 2.19及更早版本的_BSD_SOURCE或glibc 2.19及更高版本的_DEFAULT_SOURCE。 (通过        默认情况下,定义了这些宏;有关详细信息,请参阅feature_test_macros(7)。)如果是这样的话        未定义功能测试宏,然后signal()提供System V语义。
  •   

现在的问题是定义了哪一个。如果使用-std=c11进行编译,您将获得重置语义,因为它没有设置_DEFAULT_SOURCE!然后你需要每次都重新{/ 1}}

在信号处理程序中重置信号的目的是一些Unixen在触发信号时清除处理程序。还有其他有趣的边缘情况 - 使用此函数的唯一原因是它在 C标准中,但它的行为也没有很好地指定。 永远不要用它来设置自定义信号处理程序。

正如代码所说,这两个SIG_ALARM调用的应该不受欢迎。好的现代代码应该而不是signal,例如

sigaction
struct sigaction newsigfunc; newsigfunc.sa_handler = catch_alarm; sigemptyset(&newsigfunc.sa_mask); newsigfunc.sa_flags = 0; sigaction(SIGALRM, &newsigfunc, NULL); 不同,

sigaction将保证此处的可移植性;只要它不存在,signal也可能行为不端......

答案 2 :(得分:0)

简短的回答是原始的Unix实现会在收到信号后重置信号处理程序的默认值。这里的代码只是证明了这一点。

signal (sig, catch_alarm);