线程退出后无FUTEX_WAKE调用,内核为何唤醒等待线程?
问题描述
我通过strace分析正常运行的程序以对比故障程序。在正常程序中,两个基于pthreads的线程里,主线程通过常规pthread_join等待另一个线程。底层可见等待线程在futex上等待(经源码查看及GDB验证,地址匹配&pd->tid)。令我不解的是,当第二个线程退出时,该地址并无对应的FUTEX_WAKE调用:
2579217 futex(0x7fe28bfff910, FUTEX_WAIT_BITSET|FUTEX_CLOCK_REALTIME, 2579218, NULL, FUTEX_BITSET_MATCH_ANY <unfinished ...> ... 2579218 madvise(0x7fe28bf00000, 1024000, MADV_DONTNEED) = 0 2579218 exit(0) = ? 2579217 <... futex resumed>) = 0 2579218 +++ exited with 0 +++ 2579217 exit_group(0)
我曾怀疑是健壮futex ABI让内核负责唤醒,但相关列表为空。我认为若futex所在内存被解除映射,应唤醒等待线程(或触发段错误),但实际并非如此。请问内核为何会唤醒这个主线程?
解答
这是因为pthread_join的底层实现中,线程正常退出时内核会主动完成futex唤醒,不需要用户态发起FUTEX_WAKE调用,具体逻辑如下:
- 当子线程调用
exit(或线程函数返回触发隐式exit)时,内核进入线程退出处理流程。对于Linux上的NPTL(pthreads的标准实现),内核会检查该线程是否有其他线程通过pthread_join等待它。 - 你观察到的futex地址
&pd->tid,其中pd是线程描述符(struct pthread)。子线程退出时,内核会修改该tid值(比如置为0),同时直接唤醒在该futex上等待的主线程——这个唤醒动作是内核在线程退出路径中直接执行的,完全在内核态完成,不会产生用户态可见的FUTEX_WAKE系统调用,所以strace无法捕获到。 - 健壮futex针对的是持有futex的线程意外崩溃的场景,和本次线程正常退出的情况无关,所以你看到健壮futex列表为空是正常的。
- 关于内存映射的疑问:该futex所在内存属于线程描述符的一部分,要么在进程堆中,要么在线程栈上,子线程退出时不会立即解除这部分内存的映射,因此不会触发内存映射解除的唤醒逻辑,唤醒动作完全由内核的线程退出流程触发。
内容的提问来源于stack exchange,提问作者Radim Vansa
相关产品推荐
相关产品推荐

