Linux下如何触发事件以跳出线程终止时的select/poll/epoll循环
Linux线程终止事件监听方案
Linux 没有为线程提供类似进程SIGCHLD的原生终止信号,不过有和pidfd同源的线程监控机制,结合其他兼容方案可以满足直接监控线程终止、触发IO多路复用事件的需求:
1. 原生内核方案(Linux 5.2+ 推荐,最贴合需求)
内核从5.2版本开始扩展了pidfd_open()的能力,支持直接监听线程终止,逻辑和进程pidfd完全一致:
- 获取你要监控的目标线程的TID:目标线程内可以调用
gettid()获取,或者主线程通过pthread_gettid_np(pthread_t)从线程句柄转换得到 - 调用
pidfd_open(目标TID, PIDFD_NONBLOCK)得到非阻塞的线程fd(也叫tidfd) - 将这个fd加入
select/poll/epoll的监听集合,监听POLLIN事件 - 目标线程终止时,tidfd会立即触发可读事件,你可以直接跳出IO多路复用循环
注意:只要你持有未关闭的tidfd,对应线程的内核结构就不会被释放,TID不会被复用,不存在监控错位的问题。使用完毕后要主动close tidfd释放内核资源。
2. 低版本内核兼容方案
如果内核版本低于5.2无法使用tidfd,可以用以下两种稳定方案实现:
方案A:signalfd监听自定义实时信号
- 提前在进程所有线程的信号掩码中阻塞一个未被占用的实时信号(例如
SIGRTMIN+1),避免信号被默认处理逻辑丢弃 - 调用
signalfd()创建对应信号的监听fd,将其加入IO多路复用的监听集合 - 通过
pthread_cleanup_push()给目标线程注册退出清理回调,线程终止时回调内调用pthread_kill(pthread_self(), SIGRTMIN+1)发送指定实时信号 - 信号触发后signalfd会产生可读事件,即可感知线程终止
方案B:eventfd/匿名管道监听(兼容性最好)
- 提前创建非阻塞的
eventfd或者匿名管道,将读端加入IO多路复用的监听集合 - 给目标线程注册退出清理回调,线程终止时往eventfd写入1个8字节数值,或者往管道写端写入1个占位字节
- 监听端收到可读事件即可感知线程终止,该方案支持所有主流Linux内核版本
内容的提问来源于stack exchange,提问作者doron
相关产品推荐
相关产品推荐

