我看到一个代码说明如何进行安全的信号处理。我不明白为什么信号处理程序再次调用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;
}
答案 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_DFL
或SIG_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);