【发布时间】:2021-06-14 19:37:52
【问题描述】:
当我在专用线程中运行的 TCP 服务器上工作时,我注意到信号处理中的奇怪行为。我准备了以下 MWE(我使用 cerr 来避免调试打印的竞争条件):
#include <signal.h>
#include <unistd.h>
#include <iostream>
#include <thread>
#include <chrono>
using namespace std;
#undef THREAD
class RaiiObject
{
public:
RaiiObject() { cerr << "RaiiObject ctor" << endl; }
~RaiiObject() { cerr << "RaiiObject dtor" << endl; }
};
static void signalHandler(int sig)
{
write(2, "Signal\n", 7);
}
static void blockSigint()
{
sigset_t blockset;
sigemptyset(&blockset);
sigaddset(&blockset, SIGINT);
sigprocmask(SIG_BLOCK, &blockset, NULL);
}
static void setSigintHandler()
{
struct sigaction sa;
sa.sa_handler = signalHandler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, NULL);
}
void runSelect()
{
sigset_t emptyset;
sigemptyset(&emptyset);
setSigintHandler();
RaiiObject RaiiObject{};
fd_set fdRead;
while (true) {
cerr << "Loop iteration" << endl;
FD_ZERO(&fdRead);
FD_SET(0, &fdRead);
while (true) {
if (pselect(FD_SETSIZE, &fdRead, NULL, NULL, NULL, &emptyset) > 0) {
cerr << "Select" << endl;
} else {
cerr << "Select break" << endl;
return;
}
}
}
}
int main()
{
cerr << "Main start" << endl;
#ifdef THREAD
cerr << "Thread start" << endl;
//blockSigint();
thread{runSelect}.join();
#else
runSelect();
#endif
cerr << "Main exit" << endl;
return EXIT_SUCCESS;
}
当我编译一个单线程程序 (#undef THREAD) 时,我可以使用 Ctrl-C 正确终止 runSelect() 函数:
Main start
RaiiObject ctor
Loop iteration
^CSignal
Select break
RaiiObject dtor
Main exit
但是当我编译一个多线程 (#define THREAD) 程序时,它会挂在信号处理程序上:
Main start
RaiiObject ctor
Loop iteration
^CSignal
只有当我用blockSigint() 阻塞主线程上的信号时,程序才能再次按我的意愿工作。
我用strace -tt -f检查了程序,我注意到工作版本使用pselect6()和ERESTARTNOHAND:
14:46:53.543360 write(2, "Loop iteration", 14Loop iteration) = 14
14:46:53.543482 write(2, "\n", 1
) = 1
14:46:53.543586 pselect6(1024, [0], NULL, NULL, NULL, {[], 8}) = ? ERESTARTNOHAND (To be restarted if no handler)
14:46:55.286989 --- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=2707461, si_uid=1000} ---
14:46:55.287120 write(2, "Signal\n", 7Signal
) = 7
14:46:55.287327 rt_sigreturn({mask=[]}) = -1 EINTR (Interrupted system call)
14:46:55.287569 write(2, "Select break", 12Select break) = 12
14:46:55.287760 write(2, "\n", 1
但损坏的版本使用futex():
[pid 3469011] 14:48:37.211792 write(2, "Loop iteration", 14Loop iteration) = 14
[pid 3469011] 14:48:37.211916 write(2, "\n", 1
) = 1
[pid 3469011] 14:48:37.212031 pselect6(1024, [0], NULL, NULL, NULL, {[], 8} <unfinished ...>
[pid 3469010] 14:48:40.046146 <... futex resumed>) = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
[pid 3469010] 14:48:40.046256 --- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=2707461, si_uid=1000} ---
[pid 3469010] 14:48:40.046354 write(2, "Signal\n", 7Signal
) = 7
[pid 3469010] 14:48:40.046588 rt_sigreturn({mask=[]}) = -1 EINTR (Interrupted system call)
[pid 3469010] 14:48:40.046821 futex(0x7f4e5c16b9d0, FUTEX_WAIT, 3469011, NULL) = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
【问题讨论】:
-
cerr << "Signal: " << sig << endl;-- 游戏结束。 iostreams 中没有任何东西是信号安全的。您不能在信号处理程序中执行此操作。 -
@SamVarshavchik 我怀疑这是这里的问题。似乎信号正在传递到主线程
-
哦,我完全不怀疑信号正在“传递”。这不是问题所在。
-
@SamVarshavchik 我已经更改了信号处理程序,现在我使用了信号安全的
write(),但它并没有改变任何东西(即使我使用了一个空的处理程序)。 -
unix.stackexchange.com/questions/225687那里有有用的背景资料吗?这也指向SO stackoverflow.com/questions/11679568
标签: c++ linux multithreading select signals