利用glibc中鎖結構的信息解決死鎖問題

   首先非常感謝老丁和老李同學的幫助,沒有他們這個問題估計又得搞很久。遇見這個問題,真是頭疼。不熟悉代碼、不熟悉流程,但是領導還是把活給排下來了(實在不解),只能硬着頭皮找了。
問題是這樣的,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,交叉去獲取鎖,都阻塞了,都在等對方釋放鎖。出現這種問題的原因,要麼是流程設計有問題,要麼就是在某個地方獲取鎖了之後沒有正確釋放。我們的這個問題就屬於後者,在一個函數中在失敗的時候沒有釋放鎖就直接返回了......

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章