我认为我的问题不是这个问题的重复,它描述了如何找到哪个线程拥有pthread_mutex_t。我想知道如何找到内部 glibc 锁的所有者,我认为它与 pthread_mutex_t 不同。考虑从我的应用程序中获取的回溯线程,该线程是挂起的(可能是死锁?):
Thread 1 (Thread 0x7f8478e1b700 (LWP 24662)):
#0 0x00007f847cf277fc in __lll_lock_wait_private () from /lib64/libc.so.6
#1 0x00007f847cea350c in _L_lock_5314 () from /lib64/libc.so.6
#2 0x00007f847ce9c108 in _int_free () from /lib64/libc.so.6
... <unnecessary details which lead up to this thread calling free> ...
#11 0x00007f847db18ea5 in start_thread () from /lib64/libpthread.so.0
#12 0x00007f847cf19b0d in clone () from /lib64/libc.so.6
我可以使用类似于链接问题或另一个网页的任何技巧来找出哪个线程持有内部 glibc 锁吗?
其他详细信息:
注意:这个回溯是从客户端的核心文件生成的,所以我不能简单地自己测试 gdb 命令,否则我会尝试更多地使用它。
程序中还有一堆其他线程;我不会全部复制它们,但我认为调用 fork() 的线程正在持有锁:
Thread 4 (Thread 0x7f8479724700 (LWP 24775)):
#0 0x00007f847cee0b12 in fork () from /lib64/libc.so.6
...
#6 0x00007f847db18ea5 in start_thread () from /lib64/libpthread.so.0
#7 0x00007f847cf19b0d in clone () from /lib64/libc.so.6
我怀疑 fork() 可能需要与 free() 相同的锁,但我想确定这一点,并了解有关内部 libc 状态的更多信息。
这个问题的“XY”部分(我真正想知道的)是为什么线程 4 似乎没有进展。我不知道,我正在与客户合作了解更多信息,但他们说该程序在他们注意到并杀死之前被卡在这里 3 个小时。我想把这个问题集中在用 gdb 调试内部 glibc 的东西上。
注意:我知道在多线程进程中使用 fork() 的危险:子进程在调用 exec() 之前只能调用异步信号安全函数。无论如何,我的问题与孩子无关,而是与家长有关。
不幸的是,你是对的,内部 glibc 锁不是
pthread_mutex_t
。它们只是 futexes,即只是整数(pthread 互斥体也是通过底层的 futexes 实现的,但包装在包含更多信息的结构中)。 glibc 内部 lowlevellock.h
顶部的这个文档注释解释了它们是如何更详细地实现的。由于我们正在处理原始 futexes,因此您无法使用您提到的技巧来了解所有者。
根据所使用的 futex 类型,调试当前情况可能或多或少简单。一般来说,你可以有两种 futexes:
{set,get}_robust_list
) 才能正常工作。如果您正在处理强大的 futexes,您可以检查 futex 字(在 GDB 中很容易做到这一点)并恢复所有者的 TID。
现在,不幸的是(x2),在我看来,您正在处理的锁不是一个强大的 futex。特别是
__lll_lock_wait_private()
似乎并不处理 TID,而是比较/存储 futex 单词中的小常量(例如 1
或 2
)(必须检查您正在处理的确切 glibc 版本) ,您可以更改上一个链接中的版本,但仍然如此)。
如果我是对的,情况确实如此,我真的没有看到很多选择,我能想到的唯一几个是:
strace -f -e futex
检测 futex
系统调用并对输出进行推理。在每个线程中插入调试打印语句将有助于了解哪个 TID 是哪个,并且查看传递给 futex
的标志将有助于了解谁在等待哪个 futex。在死锁的情况下,您可能会看到线程永远等待 futex
suscall。futex
系统调用:参见例如本教程。与前一种情况没有太大区别,但允许您调试程序,同时还通过 eBPF 跟踪它,如果您使用 strace
则无法做到这一点(一个进程一次只能由一个跟踪器跟踪)。 最终我建议的是首先添加尽可能多的调试打印,同时确保问题仍然可重现,然后开始缩小范围。