我有一个非常大的多线程 C 应用程序(由于这个原因和其他一些原因我无法提供完整的代码)。长话短说,其中有两个线程:线程 1 将
SIGINT
发送到线程 2,而线程 2 在 ppoll()
中等待。代码看起来像这样:
主题 1:
...
if (thread && pthread_kill(&thread, SIGINT) != 0)
{
fprintf(log1, "Got error: %s", strerror((int)errno));
}
...
thread
这里是包含线程 2 句柄的 pthread_t
变量。
主题 2:
...
sigset_t mask;
int rc;
sigemptyset(mask);
...
while(true)
{
...
rc = ppoll(fds, nfds, NULL, mask);
if (rc < 0)
{
if (errno == EINTR) fprintf(log2, "Got EINTR");
else fprintf(log2, "Got error: %s", strerror((int)errno));
continue;
}
fprintf(log, "%d descriptor(s) ready", rc);
}
...
当线程 1 调用
pthread_kill
时,线程 2 预计会从 ppoll()
中删除并打印 "Got EINTR"
来记录,但由于某种原因整个进程被终止。
我目前知道的其他事情:
代码在过去按预期工作。从那时起,进行了一些看似无关的更改,以允许应用程序从 Postgres 数据库而不是 Oracle 加载数据(旧实现使用 Pro*C,新实现使用 libpq)。负责处理线程的函数的源代码没有任何改变。
我检查了哪些线程创建了哪些、正在发送哪些信号(从/到)以及所有这些发生的顺序。但旧版本和新版本之间的所有内容看起来都相同(除了线程 1 将
SIGING
发送到线程 2 时新版本停止工作,而旧版本继续工作,线程 2 成功打印 "Got EINTR"
到日志。
2.5 我绝对确定线程 1 发送
SIGINT
信号 after 线程 2 重新启动 ppoll()
。
我尝试使用
gdb
,并付出巨大的努力能够再次确认正确的线程收到SIGINT
,但遗憾的是仅此而已。
我尝试创建一个最小的可重现示例,但尚未想出一个(一切都按预期进行)
我对信号的经验很少(在多线程应用程序中使用它们的经验更少,但事实就是如此),所以我可能没有提供足够的信息,但我并不期待确切的答案 - 只是一些关于如何的想法了解可能导致此问题的原因。如果评论中指出,我将用更具体的信息更新问题。
我认为你只是缺少一个信号处理程序。
信号处置是在“每个进程”的基础上进行的。 SIGINT 的默认信号处理是终止当前进程。这意味着,如果您尚未注册信号处理程序(或完全忽略信号),则默认信号处理将决定发生什么情况。对于 SIGINT,默认是终止进程(因此终止其所有线程)。 如果您至少有一个线程未屏蔽该信号,并且您没有为该信号注册任何处理程序,则您的进程将被终止。如果您想避免这种情况并且希望能够捕获信号,则必须为其注册一个处理程序。
当线程 1 调用 pthread_kill 时,线程 2 应该从 ppoll() 中删除并打印“Got EINTR”来记录,但由于某种原因整个进程被终止。
仅当您注册了信号处理程序时才会出现这种情况。如果没有,由于 SIGINT 的默认配置,您的整个进程将按其应有的方式终止。我的建议:好好阅读
man 7 signal
,这将消除您可能存在的一些疑虑,并帮助您更好地理解信号的工作原理。