【问题标题】:How to call destructor of object on termination with Ctrl-c?如何在使用 Ctrl-c 终止时调用对象的析构函数?
【发布时间】:2019-08-15 18:06:34
【问题描述】:

我有一个对象,我想保证即使程序被 Ctrl+C 终止,它也会被破坏。

我曾尝试使用重置唯一指针的信号处理程序来执行此操作,但我被告知 std::unique_ptr::reset 禁止在信号处理程序中使用。

std::unique_ptr<MotionControl> mc;

void signal_handler(int signal_num) { 
    // destruct MotionControl object and delete pointer
    mc.reset();
    // terminate program   
    exit(signal_num);   
} 

int main(int argc, char** argv)
{
    signal(SIGINT, signal_handler);

    try {
        std::string deviceName("/dev/ttyACM0");
        mc = std::unique_ptr<MotionControl>(new MotionControl(deviceName, 119, 65, 10));
        ...
    }
    ...
}

因此,我该怎么做呢?

编辑 1:我的操作系统是 Ubuntu。

编辑2:调用析构函数失败会导致下次程序运行时硬件损坏。当调用析构函数时,我需要将一些运动控制阶段移动到安全位置。

【问题讨论】:

  • Portable C++ 对信号一无所知。这是一个操作系统的东西。探索sigset_t 和朋友。
  • 多理解一下问题就好了:运行的是什么代码?一个可以轮询标志的循环?不同的结构?此外,~MotionControl() 实际执行了什么?如果您的代码由于更严重的原因(例如 SIGKILL 或 SIGSEGV)而失败,那么调用析构函数失败是否会导致硬件损坏或物理风险?
  • 在信号处理程序中您可以合法地做的事情真的很少。析构函数实际上在做什么?
  • @RAR 如果生命或财产取决于您的清理工作,您可能需要考虑一个脱离上下文的解决方案,例如看门狗进程,以监控关键进程的任何意外终止或冻结。跨度>
  • 如果电源反弹不会损坏您的硬件,您可能会决定您的启动顺序应该首先“安全关闭硬件(以避免硬件损坏)”。这可能只是行为的短暂中断。如果您的要求允许的话。

标签: c++ c++11


【解决方案1】:

常见的“零成本”异常实现应该让您摆脱信号处理程序。但是,它可能非常不安全,因为您可能会破坏一些异步不安全状态。 (我想只有信号仅在纯异步安全代码/纯计算期间到达时才是安全的。)

#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void signal_handler(int signal_num)
{
    throw signal_num;
}
int main()
{
    try {
    struct sigaction sa;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sa.sa_handler = signal_handler;
    sigaction(SIGINT,&sa,0);

    pause();

    }catch(int X){
        printf("CAUGHT: %d\n", X);
        return 1;
    }
    return 0;
}

你最好的选择可能是设置一个全局(或线程本地,但我想这在理论上也不是可移植的)volatile sig_atomic_t 标志,让你的常规上下文不时检查它,如果它看到标志集, 那么你的常规上下文代码可能会抛出。

#include <signal.h>
#include <stdio.h>
#include <unistd.h>
volatile sig_atomic_t flag;
void signal_handler(int signal_num)
{
    flag = signal_num;
}
int main()
{
    try {
        struct sigaction sa;
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = 0;
        sa.sa_handler = signal_handler;
        sigaction(SIGINT,&sa,0);

        for(;;){
            pause();
            if (flag) throw (int)flag;
        }

    }catch(int X){
        printf("CAUGHT: %d\n", X);
        return 1;
    }
    return 0;
}

【讨论】:

  • @FrançoisAndrieux 那是无意的,只是碰巧起作用了。向 int 添加了强制转换。
  • 第二个代码示例很好。第一个代码示例可能会很糟糕 - 不要从信号处理程序中抛出。
  • @Eljay 是的,我也推荐第二个。我只提到了第一个,因为至少在 Linux 实现上应该有一些对异步异常的支持(这个例子在我的 Linux 机器上是例外的),但它仍然充满了令人讨厌的边缘情况。
【解决方案2】:

我这样做的方法是在main() 的顶部设置一个管道或一个套接字对(分别使用pipe()socketpair()),并让信号处理程序在一个上写入一个字节套接字对的套接字(或在管道 fd 对的发送 fd 上)。

你的主线程的事件循环应该是select()-ing(或poll()-ing 或其他)在另一个套接字上(或在 pipe-fd-pair 的接收-fd 上)。当它在该套接字上接收到一个字节时,主线程应该通过优雅地关闭程序来响应。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-03
    • 1970-01-01
    • 2015-08-11
    • 2014-06-13
    • 2014-05-21
    • 2021-01-20
    • 2015-12-14
    相关资源
    最近更新 更多