考虑以下测试程序
#include <csignal>
#include <iostream>
volatile std::sig_atomic_t signal_raised = 0;
void set_signal_raised(int signal) {
signal_raised = 1;
}
void check(std::sig_atomic_t expected) {
if (signal_raised != expected) {
std::cerr << signal_raised << " != " << expected << std::endl;
abort();
}
}
int main() {
using namespace std;
check(0);
std::signal(SIGINT, set_signal_raised);
check(0);
std::raise(SIGINT);
check(1);
signal_raised = 0;
check(0);
std::raise(SIGINT);
check(1);
cerr << "OK.\n";
}
使用 GCC 和 Clang,它输出“OK”。但是,使用 Visual Studio 2015,它不会输出任何内容。
信号处理程序在处理第一个信号后重置。这可以通过添加
来验证auto prev = std::signal(SIGINT, set_signal_raised);
if (prev != set_signal_raised) {
std::cerr << "Unexpected handler." << std::endl;
abort();
}
到检查功能。这是允许的和预期的吗?
最佳答案
信号处理的重置是 Unix System V 使用的行为。但 BSD(当前为 glibc)不会重置信号处理。 POSIX 标准允许这两种行为。 C标准没有规定是否允许“重置”。
来自signal(2)
:
POSIX.1 solved the portability mess by specifying sigaction(2), which provides explicit control of the semantics when a signal handler is invoked; use that interface instead of signal().
In the original UNIX systems, when a handler that was established using signal() was invoked by the delivery of a signal, the disposition of the signal would be reset to SIG_DFL, and the system did not block delivery of further instances of the signal. This is equivalent to calling sigaction(2) with the following flags:
sa.sa_flags = SA_RESETHAND | SA_NODEFER;
System V also provides these semantics for signal(). This was bad because the signal might be delivered again before the handler had a chance to reestablish itself. Furthermore, rapid deliveries of the same signal could result in recursive invocations of the handler.
因此,Visual studio 似乎遵循 System V 行为。
Is this allowed and expected?
这是允许的,但肯定不受欢迎。为此,POSIX推出了sigaction()
。如果您有 sigaction() 则使用它。
否则,您只需要每次在信号处理程序内重新安装处理程序:
void set_signal_raised(int signal) {
std::signal(SIGINT, set_signal_raised);
signal_raised = 1;
}
关于c++ - Visual C++ 2015 中的 SIGINT 处理程序重置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41552081/