首先,我使用pthread库编写多线程C程序。线程总是被等待的互斥锁挂起。当我使用strace实用程序查找线程处于FUTEX_WAIT
状态时,我想知道当时哪个线程持有该互斥量。但是我不知道该怎么做。有没有公用事业可以做到这一点?
有人告诉我Java虚拟机支持此功能,因此我想知道Linux是否支持此功能。
Answers:
您可以使用互斥量内部知识来执行此操作。通常,这不是一个好主意,但是对于调试来说很好。
在具有NPTL实现的pthreads Linux(任何现代glibc)下,您可以检查结构的__data.__owner
成员pthread_mutex_t
以找出当前已将其锁定的线程。这是通过以下方法将其附加到流程之后的方法gdb
:
(gdb) thread 2
[Switching to thread 2 (Thread 0xb6d94b90 (LWP 22026))]#0 0xb771f424 in __kernel_vsyscall ()
(gdb) bt
#0 0xb771f424 in __kernel_vsyscall ()
#1 0xb76fec99 in __lll_lock_wait () from /lib/i686/cmov/libpthread.so.0
#2 0xb76fa0c4 in _L_lock_89 () from /lib/i686/cmov/libpthread.so.0
#3 0xb76f99f2 in pthread_mutex_lock () from /lib/i686/cmov/libpthread.so.0
#4 0x080484a6 in thread (x=0x0) at mutex_owner.c:8
#5 0xb76f84c0 in start_thread () from /lib/i686/cmov/libpthread.so.0
#6 0xb767784e in clone () from /lib/i686/cmov/libc.so.6
(gdb) up 4
#4 0x080484a6 in thread (x=0x0) at mutex_owner.c:8
8 pthread_mutex_lock(&mutex);
(gdb) print mutex.__data.__owner
$1 = 22025
(gdb)
(我切换到挂起的线程;进行回溯以查找被pthread_mutex_lock()
卡住的线程;更改堆栈框架以查找其试图锁定的互斥体的名称;然后打印该互斥体的所有者)。这告诉我,具有LWP ID 22025的线程是罪魁祸首。
然后,您可以使用thread find 22025
查找该gdb
线程的线程号并切换到该线程号。
.__data.__owner
TID。当每个线程启动时,您可以让他们记录其TID(使用tid = syscall(SYS_gettid);
)以及其TID pthread_t
(来自pthread_self()
)。
stat
文件中检查线程的堆栈指针proc
,它的值非常接近(几kb之内)pthread_t
。:-)
info threads
用来将TID(.__data.__owner
)映射到pthread ID(一个在gdb中进行操作的ID)。
我不知道有任何这样的功能,因此我认为您不会轻易摆脱这种情况-而且它可能不会像您认为的那样有助于调试程序。尽管技术水平很低,但日志记录是调试这些事情的朋友。开始收集您自己的小日志功能。他们不必花哨,而只需要在调试时完成工作即可。
对不起,C ++但类似:
void logit(const bool aquired, const char* lockname, const int linenum)
{
pthread_mutex_lock(&log_mutex);
if (! aquired)
logfile << pthread_self() << " tries lock " << lockname << " at " << linenum << endl;
else
logfile << pthread_self() << " has lock " << lockname << " at " << linenum << endl;
pthread_mutex_unlock(&log_mutex);
}
void someTask()
{
logit(false, "some_mutex", __LINE__);
pthread_mutex_lock(&some_mutex);
logit(true, "some_mutex", __LINE__);
// do stuff ...
pthread_mutex_unlock(&some_mutex);
}
日志记录不是一个完美的解决方案,但没有。它通常会为您提供所需的知识。
pthread_mutex_*
用记录了函数调用,互斥体的地址和线程标识符的东西包装函数(pthread_t
在Linux上碰巧是不可或缺的类型,不是可移植的假设,而是很方便的)。
malloc
在某些地方不安全-例如,在信号处理程序,atfork处理程序中,多线程程序中的fork和exec之间,等等。请参见async-signal-safety和其他手册页。
通常,libc / platforms调用由OS抽象层抽象。可以使用所有者变量和pthread_mutex_timedlock跟踪互斥锁死锁。每当线程锁定时,都应使用自己的tid(gettid()更新该变量,也可以为pthread id存储使用另一个变量)。因此,当其他线程阻塞并在pthread_mutex_timedlock上超时时,它可以显示所有者tid和pthread_id的值。这样,您可以轻松地找到所有者线程。请在下面找到代码段,请注意,并未处理所有错误情况
pid_t ownerTid;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
class TimedMutex {
public:
TimedMutex()
{
struct timespec abs_time;
while(1)
{
clock_gettime(CLOCK_MONOTONIC, &abs_time);
abs_time.tv_sec += 10;
if(pthread_mutex_timedlock(&mutex,&abs_time) == ETIMEDOUT)
{
log("Lock held by thread=%d for more than 10 secs",ownerTid);
continue;
}
ownerTid = gettid();
}
}
~TimedMutex()
{
pthread_mutex_unlock(&mutex);
}
};
还有其他方法可以找出死锁,也许此链接可能对http://yusufonlinux.blogspot.in/2010/11/debugging-core-using-gdb.html有所帮助。
请阅读下面的链接,它具有用于查找锁所有者的通用解决方案。即使在库中锁定并且您没有源代码,它也可以工作。
https://zh.wikibooks.org/wiki/Linux_Applications_Debugging_Techniques/Deadlocks