我认为我的问题不是this question的重复,this question描述了如何找到哪个线程拥有pthreadmutex_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

我可以使用任何类似于链接的问题或this other webpage的技巧来找出哪个线程持有内部glibc锁吗?

其他详细信息:

注意:此回溯是从客户端的核心文件生成的,因此我不能自己简单地测试GDB命令,否则我会try 更多地使用它.

程序中还有一堆其他线程;我不会全部复制它们,但我确信调用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,也就是仅仅是整数(p线程互斥体也是在幕后使用futex实现的,但包装在一个包含更多信息的 struct 中).Glibc中内部lowlevellock.h顶部的This doc-comment更详细地解释了它们是如何实现的.因为我们是在处理未加工的绒布,所以你不能用你提到的诡计来了解店主.

根据所使用的Futex的类型,调试您的当前情况可能或多或少很简单.有两个类别:

  • 正常的绒布.它们通常被实现为小整数,它们不会自己跟踪所有者(它们可以借助其他东西来跟踪所有者,但Futex字本身并不包含所有者信息).
  • Robust个Futex(见this introduction to robust futexesrobust futex ABI documentation).这些确实是赛道所有者的一种机制,可以在持有Futex时从崩溃等特殊情况中恢复过来.Futex单词包含所有者线程ID,可选地使用一些标志进行OR(请参阅前面的文档链接).它们的管理比普通Futex的管理稍微复杂一些,而且它们还需要多几个syscall({set,get}_robust_list)才能正常工作.

如果您正在处理健壮的Futex,您可以判断Futex Word(在GDB中很容易做到这一点)并恢复所有者的TID.

不幸的是(X2)在我看来,您正在处理的锁是not,一个健壮的Futex,但却是一个普通的锁.特别是,__lll_lock_wait_private()似乎不处理TID,并比较/存储Futex Word中的小常量(例如12)(用户必须判断您正在处理的glibc版本,您可以在上一个链接中更改版本,但仍然可以).

如果我是正确的,情况就是这样,我真的看不到太多 Select ,我能想到的只有几个:

  • 如果您不能自己运行测试并且只能访问核心转储,请分发使用调试符号编译的应用程序版本,添加一些日志(log)记录以与核心转储一起收集,如果不可能,可以添加一些内部逻辑以将每个线程的信息写入内存中的特定区域,以便将其包括在核心转储中.
  • 如果您可以运行应用程序并在本地复制该问题,请try 使用strace -f -e futex来检测futex个系统调用并对输出进行推理.在每个线程中插入调试打印语句将有助于了解哪个TID是哪个TID,查看传递给futex的标志将有助于了解谁在等待哪个Futex.在死锁的情况下,您可能会看到一个线程永远等待futex系统调用.
  • 或者try 编写一个eBPF过滤器来跟踪futex个系统调用:参见this tutorial.与前面的用例没有太大不同,但允许您在调试程序的同时通过eBPF跟踪它,如果您使用的是strace,则不能这样做(一个进程一次只能由一个跟踪器跟踪).您还可以在生产环境中这样做,因为eBPF跟踪点实际上是分开的,不会干扰跟踪.

不过,我最终建议的是从添加尽可能多的调试印记开始,同时确保问题仍然可以重现,然后开始缩小范围.

C++相关问答推荐

根据工具链文件中的定义替换单个函数定义

带双指针的2D数组

不同到达时间的轮询实现

为什么GCC在每次循环迭代时都会生成一个数组的mov&S使用[]访问数组?(-03,x86)

如何在C客户端应用程序的ClientHello消息中添加自定义扩展?

C中函数类型的前向声明

为静态库做准备中的奇怪行为

如何在下面的C代码中正确管理内存?

如何在STM8项目中导入STM8S/A标准外设库(ST VisualDeveloper)?

FRIDA-服务器成为端口扫描的目标?

为什么双精度d=flt_max+flt_max;在c语言中得到inf的结果

如何使用_newindex数组我总是得到错误的参数

如何确保我将使用C标准库函数的函数版本,如&getc";,而不是类似函数的宏版本?

在C程序中使用Beaglebone Black UART的问题

如何使用空元素块声明指针数组

如何在C宏定义中包含双引号?

分配给静态变量和动态变量的位置之间有区别吗?

我的代码可以与一个编译器一起使用,但不能与其他编译器一起使用

C循环条件内的函数

在 C 中传递参数时出现整数溢出