Visual C++2015中的SIGINT处理程序重置

SIGINT handler reset in Visual C++ 2015

本文关键字:程序 处理 SIGINT C++2015 中的 Visual      更新时间:2023-10-16

考虑以下测试程序

#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通过指定sigaction(2)解决了可移植性问题当信号处理程序调用;使用该接口而不是signal()。

在最初的UNIX系统中,当using signal()是通过传递信号调用的信号的处理将重置为SIG_DFL,并且系统没有阻止信号的进一步实例的传递。这是相当于用以下标志调用sigaction(2):

sa.sa_flags=sa_RESETHAND|sa_NODEFER;

系统V还为signal()提供了这些语义。这太糟糕了因为信号可能在处理程序具有重新建立自己的机会。此外相同的信号可能导致处理程序的递归调用。

因此,Visual studio似乎遵循System V的行为。

这是允许的还是预期的?

这是允许的,但肯定不可取。为此,POSIX引入了sigaction()。如果您有sigaction(),则使用它。否则,您只需要每次在信号处理程序中重新安装处理程序:

void set_signal_raised(int signal) {
std::signal(SIGINT, set_signal_raised);
signal_raised = 1;
}