POSIX线程/信号:确定信号传递到哪个线程的可移植方法?

8
我有一个多线程服务器(使用POSIX线程),每个持久连接都有一个线程。在其中一个线程中,连接的另一端关闭了,导致SIGPIPE信号被传递。是否有一种(最好是可移植的)方法来确定发生了哪个线程(因此是哪个连接),以便我的信号处理程序可以自己执行线程/连接清理工作或设置标志,以便主线程和工作线程分别看到它们需要稍后执行该操作?
编辑:我想知道是否可以使用&errno,在全局数组中存储它并将其与线程的标识符相关联,然后在信号处理程序中搜索&errno。线程特定的errno是否对信号处理程序可见?我对线程安全的errno的理解是否正确?

如果将信号传递给特定的线程,则该线程将处理该信号。这就是将信号传递到特定线程的含义。我不明白有什么需要检查的? - CB Bailey
你为什么关心线程而不是套接字本身? - bestsss
首先,我需要能够告诉主线程,它可以释放与连接处理线程相关的数据。为了做到这一点,主线程需要知道哪个特定的连接(因此是索引到其连接/线程信息数组中)正在终止。为了做到这一点,信号处理程序需要知道它是从“连接线程28”还是“连接线程91”调用的,以便它可以告诉主线程“为#28进行清理”或“为#91进行清理”。我正在尝试弄清楚如何从信号处理程序内部获取此信息,以便可以发送它。 - Steely Dan
我已经找到了一些关于在许多系统中在信号处理程序中使用pthread_self是安全的轶事证据,但我仍在寻找明确的参考资料。 - CB Bailey
也许你应该澄清你的问题标题。目前它说你想确定一个信号被传送到哪个线程,但这似乎并没有帮助你解决你的问题。 - CB Bailey
显示剩余5条评论
4个回答

12

我不这么认为。对于多线程服务器,更好的解决方案是在程序启动时通过调用signal(SIGPIPE, SIG_IGN)来禁止SIGPIPE信号,并让线程处理send()返回的错误值(-1/EPIPE)。

信号和多线程不太兼容。


我曾考虑过这种可能性;问题在于,如果这样做,我就无法检测到断开连接,直到下一次需要通过该连接发送数据——这可能永远不会发生。 - Steely Dan
1
这可以通过仅选择该fd进行错误捕获来轻松解决。 - Eugen Rieck
1
嗯,你说得对。我正在使用 epoll,并忘记了 EPOLLHUP。 - Steely Dan
我认为你会发现,“在下次需要向该连接发送数据之前不会检测到断开连接”的问题是一个单独的问题,无论你是否使用信号都必须处理它。默认情况下,TCP在长时间(如果有的话)内不会检测到空闲连接已被切断,这取决于主机的TCP保持活动选项设置。在某些操作系统(例如Linux)上,您可以基于每个套接字设置保持活动选项(通过setsockopt(fd,SOL_TCP,TCP_KEEPIDLE)),而在其他操作系统上(如Windows)则是一个全局设置。 - Jeremy Friesner
4
正确的做法是 - 所有 网络服务器,不仅仅是多线程的,都应该忽略 SIGPIPE 信号,改为处理 EPIPE 错误。@SteelyDan: SIGPIPE 信号并不会比 EPIPE 更快地被处理 - 该信号只会在会否发生 EPIPE 错误时生成。 - caf
显示剩余2条评论

2

您可以在信号处理程序中安全地使用pthread_self。它是异步信号安全的

这个答案是为了后人而留下的。我不建议从技术上采用这种方法。其他回答和评论提出的不同方法将更好地实现。

当时提出这个问题时,pthread_self的安全性并不明显。 POSIX:2008技术勘误1于2013年(此问题提出两年后)发布,澄清了pthread_self明确是异步信号安全的,如Linux signal(7) man page中所述。

相关地,您在评论中提到您不确定"内核是否向任何没有阻塞的线程发送[SIGPIPE]"。这是一个很好的担忧——它是进程定向信号还是线程定向信号?——幸运的是,这是一个错误的担忧。内核生成的SIGPIPE是响应某些I/O调用而"synchronously generated"的信号,并发送到调用线程。这在Corrigendum 2(2004)中明确说明,POSIX:2001的SIGPIPE响应write"发送到线程"(强调添加)。


0

据我所知,线程本地存储函数不是信号安全的。 - Steely Dan
@Steely Dan:我并不是在指使用函数来访问特定于线程的数据。请看我刚刚在帖子中添加的参考资料,了解我所指的内容。 - alk

0

我的方法是为SIGPIPE的信号处理程序编写一个将其线程ID写入全局变量的函数,并使用其他机制向应该处理它的线程发出信号。


那仍然引发了同样根本的问题,即我有什么便携式、信号安全的方法可以找出发生这种情况的特定线程,因为我必须弄清楚线程ID是什么。 - Steely Dan
这很容易:例如,通过您的系统支持的任何事件机制(包括将某些内容发送到管道)引发事件,并让一个线程监听这些事件(其中包括在该管道上进行选择),必要时读取(受锁保护的)全局变量并对其进行处理。 - Eugen Rieck
我认为全局变量方法存在的问题是,如果两个信号快速连续地被触发,第二个信号可能会在第一个处理程序执行之前覆盖第一个信号写入的值。你至少需要一个受锁保护的FIFO队列...但是然后你会遇到第二个问题,即不允许从信号处理程序内部锁定互斥量。 - Jeremy Friesner
很抱歉,我不确定我理解你的建议。您是说信号处理程序向事件管理线程发送某些内容,然后通过查看(例如)接收到的特定管道,事件管理线程知道事件通知来自哪个线程?我曾考虑过基本相似的架构,但因为每个连接需要三个文件描述符(一个用于套接字,另外两个用于管道的两端),这会比我想象中更快地达到限制。 - Steely Dan
@SteelyDan 根据你对“可移植”的定义,可能有更简单的方法,最可移植的方法是使用管道,但你应该意识到,你不需要一直连接它 - 只需在接收信号后连接即可! - Eugen Rieck
我认为使用命名管道可以实现:主线程一直保持一端开放并对其进行轮询,而工作线程仅在需要时打开另一端。但在这种情况下,防止恶意(或考虑不周的)外部程序(或其他用户)访问它变得很重要。到目前为止,我只考虑过pipe(2)函数中的管道。 - Steely Dan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接