调试和自由执行中的信号处理

Signal handling in debug and in free execution

本文关键字:信号处理 执行 自由 调试      更新时间:2023-10-16

我必须在使用Boost.Asio的程序中处理SIGINTSIGTERM。我为此使用boost::asio::signal_set::async_wait()

问题是信号处理程序仅在我简单地运行应用程序时才获得控制权,而在调试它时则不会。

下面是一些代码:

Proxy::Proxy():
signals_(ioContext_, SIGINT, SIGTERM)
{
signals_.async_wait(
[this](const boost::system::error_code& error, int)
{
if (!error)
ioContext_.stop();
}
);
}
Proxy::run()
{
ioContext_.run();
}

当我们run()Proxy时,ioContext_开始处理事件。如果我们只是简单地运行程序并在终端中执行Ctrl+C,则信号处理程序(即 lambda(会停止ioContext_(正如我们预期的那样(,io_context::run将控制权交还给我们。但是在调试模式下,程序会对Ctrl+C做出反应,但执行在epoll_wait()的某个地方停止。如果我们继续执行,它会挂在epoll_wait()的某个地方,依此类推。

下面是执行停止位置的堆栈跟踪:

epoll_wait
boost::asio::detail::epoll_reactor::run
boost::asio::detail::scheduler::do_one_run
boost::asio::detail::scheduler::run
boost::asio::io_context::run
Proxy::run
main

为什么它会在调试模式下发生,而在其他模式下不会发生?

这里的问题是GDB使用SIGINT作为中断程序并允许您开始调试的机制。

(gdb) info signals SIGINT
Signal        Stop      Print   Pass to program Description
SIGINT        Yes       Yes     No              Interrupt

这是说 GDB 不应该将SIGINT传递给程序,而应该使用它来停止程序并将您放入 GDB 提示符。将其发送到程序的最简单机制是在此时从 GDB 发送信号:

(gdb) signal SIGINT

现在,您的程序应按预期继续运行。


根据您执行此操作的频率,键入signal SIGINT可能会变得不方便。幸运的是,GDB 允许您修改它处理信号的方式。您希望SIGINT不停止程序(将您放入 GDB 提示符(并将其传递给程序。

(gdb) handle SIGINT nostop pass
SIGINT is used by the debugger.
Are you sure you want to change it? (y or n) y
Signal        Stop      Print   Pass to program Description
SIGINT        No        Yes     Yes             Interrupt

我们现在处于"有点不可取"的状态,因为我们不能再使用 Ctrl+C 跳转到我们的 GDB 提示符。您必须依赖预设断点和其他机制。

如果要获得更高级,可以使用catchcommands来确定SIGINT的来源(从调试使用 SIGINT 和 gdb 的程序中提取(:

catch signal SIGINT
commands
if $_siginfo._sifields._kill.si_pid == 0
print "Received SIGINT from tty"
else
printf "Received SIGINT from %d; continuingn", $_siginfo._sifields._kill.si_pid
signal SIGINT
end
end