【发布时间】:2020-04-07 07:45:51
【问题描述】:
我已经为设计为在 linux 上运行的应用程序设置了如下所示的 C++ 信号处理程序:
设置信号处理程序以调用静态函数:
// Setup the SIGNTERM signal handler for kill/pkill or systemd terminate
if (signal(SIGTERM, manager_signal_handler) == SIG_ERR)
{
ERROR << "Failed to add signal SIGTERM to signal handler with error code: " << std::strerror(errno) << ENDL;
}
静态信号处理程序——调用类实例成员函数
static dds_manager_base *p_this_dds_manager_base = nullptr;
static void dds_manager_signal_handler(int signum)
{
if (p_this_manager_base)
{
DEBUG << "static manager_signal_handler calling: p_this_manager_base->signal_handler\n";
p_this_manager_base->signal_handler(signum);
}
else
{
ERROR << "manager_signal_handler - manager pointer not set\n";
}
}
成员函数 - 处理信号:
void dds_manager_base::signal_handler(int signum)
{
DEBUG << "dds_manager_base::signal_handler - received signal: " << signum << ENDL;
// Termination actions - can take 5-10 seconds...
}
我正在使用sytemctnl powerdown 来关闭 linux(这会停止运行我的应用程序的服务)。我注意到我的应用程序接收到 2 个甚至 3 个 SIGTERM 信号,间隔大约 500 毫秒。
我可以处理这种情况,但我期望的是 1 个 SIGTERM,如果我的应用程序没有在该时间限制内终止,那么可能会在 90 秒后发出 SIGABRT。
我的问题是,在某些情况下,一旦我处理了信号,我的应用程序已经终止得足够快,以至于我的类不再存在,并且我得到一个核心转储,因为 signal_handler() 函数不再存在。因此,一旦我处理了信号,我就有想法将信号返回给default behaviour signal(SIGTERM, SIG_DFL); - 但是由于systemd在第二次调用std::exit()(我相信这是默认行为)时调用SIGTERM不止一次(我相信这是默认行为)被调用并终止我的应用程序才能正常关闭。
这是我的服务/单元文件:
[Unit]
Description=Invoke script to start the corvus application
After = network.target mnt-appdata.mount
[Service]
Type=idle
ExecStart=/mnt/appdata/deploy/services/start_module.sh ${SYS_NUM} ${DDS_NUM}
[Install]
WantedBy=multi-user.target
您可以看到脚本 start_module.sh 被调用 - 这就是运行我的应用程序的方法:./my-application &
所以我的问题是: - 为什么我得到不止一个 SIGTERM 并且如此接近? - 我能做些什么来解决这种行为?
【问题讨论】:
-
与您的问题不严格相关,但如果您的
ERROR或DEBUG是 C++ 流或类似的,则不能在信号处理程序中使用它们。只有a very restricted set 的 POSIX 系统调用和 C 库函数可以在信号处理程序中使用,特别是不包括任何用于 IO 或内存分配的标准 C 和 C++ 工具。由于在信号处理程序中几乎不可能做任何有意义的事情,所以您要做的就是引发sig_atomic_t标志并让主程序处理关闭。 -
为什么不在启动 SIGTERM 函数之前设置一个标志。如果标志已经设置,让信号处理程序什么都不做。使用互斥锁保护标志,或使用
std::atomic。 -
systemd发出第一个关机信号后,你的应用需要多长时间才能关机? Sysemd 有一种机制,它只是想确保您的应用程序在关闭电源之前关闭。 (您可以设置一些设置以防止关机继续进行,除非您的应用程序并且直到您的应用程序结束释放锁定以防止关机。systemd 发送信号进行检查没有任何问题,如果您已经开始关机过程 - - 你为什么不能忽略它们?
-
@super 在信号处理程序中使用互斥锁是在信号处理程序和“常规”代码之间获得死锁的最佳方法。此外,
std::atomic<T>不是异步信号安全的,除非给定的std::atomic您正在使用is_lock_free() == true(我什至不确定它是否得到保证)。信号!=线程,不要混淆他们的东西。使用sig_atomic_t并对此感到满意。 -
另外:你的应用程序是多线程的吗?