利用glibc中锁结构的信息解决死锁问题

Posted on 2013-07-28 21:50  冰天雪域  阅读(184)  评论(0编辑  收藏  举报
   首先非常感谢老丁和老李同学的帮助,没有他们这个问题估计又得搞很久。遇见这个问题,真是头疼。不熟悉代码、不熟悉流程,但是领导还是把活给排下来了(实在不解),只能硬着头皮找了。
问题是这样的,cache服务器中有一个存储对象的哈希表,每次访问哈希表时都要获取hash_rwlock读写锁,现在进程在获取读锁时死锁。使用gdb进入3个worker进程,发现死锁的位置都一样,都是在获取hash_rwlock读锁时阻塞住了。遇见这样的问题,加上对代码不熟,真是各种犯二。因为reload的关系,进程其实总共有9个,但是3个是一组的,所以只看了其中的一组,没有全部查看,如果全部查看的话,估计可以更早发现问题。
在同事的提醒下,先在阻塞的位置打印锁的结构,如下所示:

找了glibc的源码来看,发现pthread_rwlock_t结构的成员竟然完全没有注释,只好去看加锁、解锁的函数。在获取读锁时,只有在__writer成员为0并且__nr_writers_queued为0,即没有进程获取写锁或等待获取写锁时,才会获取锁成功。获取读锁成功后,会将__nr_readers成员加1.这么看来的话,__nr_readers存储的是当前获取读锁的进程或线程数量。在获取写锁时,只有在__writer成员为0,并且__nr_readers为0,即没有进程或线程获取写锁或读锁时,才会获取写锁成功。获取写锁成功后,会将__writer成员设置为线程ID(如果是单进程,则为进程ID),如下所示:
rwlock->__data.__writer = THREAD_GETMEM (THREAD_SELF, tid);
最初没有注意到这行代码,自己写了一个获取写锁的测试程序,然后用gdb打印出来的。这个方法虽然笨,没有思路的时候或许着急的时候,也是不错的。
有了这些信息,再看上面的图,可以发现很多信息。__nr_readers为0,表示没有进程或线程获取hash_rwlock的读锁;__writer为12959,说明进程ID为12959的进程正在获取hash_rwlock的写锁。
现在一下子找到了方向,立马gdb进入12959进程,发现它阻塞在获取另一个进程锁process_lock(类型为pthread_mutex_t),打印process_lock,如下图所示:
   pthread_mutex_t类型中的__owner中存储的是获取当前互斥锁的进程或线程ID。找到了互斥锁的持有者,再gdb进入12960进程,发现阻塞在获取hash_rwlock读锁的位置。
至此问题就很明了了,典型的死锁。A进程获取了锁m,然后去获取另一个锁n,而B进程获取了锁n,然后去获取了另一个锁m,交叉去获取锁,都阻塞了,都在等对方释放锁。出现这种问题的原因,要么是流程设计有问题,要么就是在某个地方获取锁了之后没有正确释放。我们的这个问题就属于后者,在一个函数中在失败的时候没有释放锁就直接返回了......

 

Copyright © 2024 冰天雪域
Powered by .NET 8.0 on Kubernetes