【问题标题】:Signal Handling (c++)信号处理 (c++)
【发布时间】:2021-09-07 01:06:04
【问题描述】:

我有一个简单的代码循环“信号很酷”这个词接收“SIGINT”信号我如何添加更多信号以及如何控制我的程序在用户触发信号时显示的内容。

// ConsoleApplication3.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <csignal>

using namespace std;

void signalHandler(int signum) {
    cout << "Interrupt signal (" << signum << ") received.\n";

    // cleanup and close up stuff here  
    // terminate program  

    exit(signum);
}

int main() {
    // register signal SIGINT and signal handler  
    signal(SIGINT, signalHandler);
    

    while (1) {
        cout << "SIGNALS ARE COOL" << endl;
        
    }
    

    return 0;
}

【问题讨论】:

  • 信号处理程序非常受限于它可以/不能做什么。例如,它不能执行大多数 I/O(它可以执行 readwrite)或内存分配/释放。请参阅man 2 sigaction 了解可以执行的操作列表。

标签: c++ signals sigint signal-handling


【解决方案1】:

正如@Eljay 在 cmets 中已经提到的,你必须小心你在信号处理程序中所做的事情。

我也不建议using namespace std,但这是另一个故事link

我向您推荐这个page,它根据 c++ 标准解释了很多关于信号可以做什么和不能做什么的信息。现在它们在您的编译器(我假设是 MSVC)中实际执行的操作可能会有所不同。

一些重要的部分,如前所述,你不应该做 I/O,你不应该扔,等等......

要回答您的问题,您是在正确的轨道上,可以通过以下方式添加其他信号:

// catch SIGTERM
std::signal(SIGTERM, signalHandler);
std::signal(SIGSEGV, signalHandler);
std::signal(SIGINT, signalHandler);
std::signal(SIGABRT, signalHandler);
// insert others

然后,我建议将信号的值存储到某个原子变量中,例如:gSignalThatStoppedMe

std::atomic<int> gSignalThatStoppedMe = -1;

// I also added 'extern "C"' because the standard says so
extern "C" void signalHandler(int signum) {
  gSignalThatStoppedMe.store(signum);
}

然后,您的 while 循环将检查 != -1,或者为此选择另一个值,我没有检查某些实现是否使用 -1 作为信号的有效值

// ...
while(gSignalThatStoppedMe.load() == -1)
{
  // your old code
}

现在,使用里面的值进行各种切换,并输出停止它的信号,例如:

switch(gSignalThatStoppedMe.load())
{
  case SIGINT:
    std::puts("It was SIGINT");
    break;
  case SIGTERM:
    std::puts("It was SIGTERM");
    break;
  default:
    break;
}

我认为这具有较少的未定义行为,这总是一件好事。

编辑:这是一个编译器资源管理器link

CTRL-C 的输出:

SIGNALS ARE COOL
SIGNALS ARE COOL
SIGNALS ARE COOL
SIGNALS ARE COOL
SIGNALS ARE COOL
SIGNALS ARE COOL
SIGNALS ARE COOL
SIGNALS ARE COOL
SIGNALS ARE COOL
Interrupt signal SIGINT (2) received.

【讨论】:

  • 除了 {getSignalThatStoppedme} 还有其他方法吗?我试过了,它没有用我不知道我是否做错了什么,但有没有办法以更简单的方式捕捉信号,如果不是,你能帮我把你所做的放在我的代码中吗?无论如何都明白谢谢你的帮助。
  • @HadeZedan 添加了编译器资源管理器链接
  • 非常感谢,但我有一个问题,所以如果我按下 control+c,它会显示 SIGINT 信号,但我如何捕捉其他 3 个信号(SIGFPE.SIGABRT,SIGSEGV)跨度>
  • 在我发送的链接的第 15,16 行,您可以找到对 std::signal 函数的调用。您需要使用您提到的信号添加 nore 调用。像std::signal(SIGFPE, signalHandler); std::signal(SIGABRT, signalHandler); std::signal(SIGABRT, SIGSEGV); 这样的东西。在 Windows 上我不知道如何做其他的,但在 Linux 上你可以使用 kill -&lt;SIGNALNUMBER_HERE&gt; &lt;PID&gt;
【解决方案2】:

我发现这看起来像是一个作业;所以我所说的可能与你无关(但有一天可能与某人有关)。

--编辑--

我看到你也有stdafx.h,我认为这是一个 Visual Studio Windows 的东西,在这里我建议一个 POSIX 解决方案(不是纯 C++)。我没有仔细阅读,这使我的整个答案无效(我认为)。您可能无法使用我的建议,对此我很抱歉。

但是,我将把它留在这里,以防有一天有人发现它并需要在 Unix 系统中处理信号。

--

我发现完全避免像这样的信号处理函数并按照自己的方式获取信号通常更实际很多。正如其他人所指出的,有很多关于在信号处理程序中可以做什么和不能做什么的规则,因为它们可以在任何时间、任何线程中调用,除非您采取额外的预防措施。我已经看到这个结果导致了很多乱七八糟的代码,比如“有一个全局的bool got_signal,它会被整个应用程序的所有东西检查以知道它们是否应该关闭”。显然有很好的方法来进行信号处理,但在这一点上,我尝试完全避免它,转而支持其他选择。

函数pthread_sigmasksigwait 可用于在此处反转控制,并允许您在定义的程序执行流程中接受您想要的信号,然后您无需担心采取无效操作当你处理它们时。使用pthread_sigmask,您可以告诉操作系统不要中断您的程序以传递信号,而是将它们排队,然后sigwait 可以用于在适当的时间处理它们。您不能对所有信号都执行此操作(kill -9SEGFAULT 等某些信号不能/不应该被忽略),但它适用于大多数信号。

使用这样的方法,在大型应用程序中与信号交互也非常容易。您可以在main 的开头阻止信号,这将传播到所有子线程,然后您可以指定一个特定的子线程来等待信号,然后以适合的任何方法将事件传递到应用程序的其余部分您的应用程序的框架。

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

#include <initializer_list>
#include <functional>
#include <algorithm>
#include <iostream>

sigset_t make_sigset(std::initializer_list<int32_t> signals)
{
    sigset_t set;
    const int32_t result = sigemptyset(&set);

    std::for_each(signals.begin(), signals.end(), std::bind(&sigaddset, &set, std::placeholders::_1));

    return set;
}

int main()
{
    const auto signal_list = make_sigset({SIGTERM, SIGSEGV, SIGINT, SIGABRT});

    pthread_sigmask(SIG_BLOCK, &signal_list, nullptr);

    int32_t last_signal;
    do
    {
        sigwait(&signal_list, &last_signal);
        std::cout << "Got signal " << last_signal << std::endl;

        // Exit on sigint so ctrl+c still works
    } while (last_signal != SIGINT);

    return 0;
}

【讨论】:

  • 感谢您的评论,但是当我尝试您的代码(sigset_t,sigemptyset int32_T,)未定义时,您能帮我吗
  • 在顶部查看我的编辑。这可能是因为您使用的是 Windows,而我只是假设您使用的是 Unix 系统;此方法仅适用于 Unix 或其他具有完整 POSIX 信号支持的系统。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-23
  • 2011-01-29
  • 2021-05-19
  • 2013-11-19
相关资源
最近更新 更多