關於 mutex 的調試實例

今天解決了一個關於 mutex 的 bug.
程序每次登錄成功就會創建兩個線程.  sign out 的時候可能會強制 Terminate 這兩個線程.
當程序出現問題時用 Process Explorer 觀察各個線程. 發現其中一個線程一直在等一個對象, 
打開 windbg, 查看 WaitForSingleObject() 的參數. 找到了這個對象的句柄 6d0. 在 windbg 中執行:

0:001>!handle 6d0 f
Handle 6d0
  Type          Mutant
  Attributes    0
  GrantedAccess 0x1f0001:
         Delete,ReadControl,WriteDac,WriteOwner,Synch
         QueryState
  HandleCount   4
  PointerCount  8
  Name         none
  Object Specific Information
    Mutex is Owned
 
知道了這是一個 mutex. 我用 windbg 也不太熟. google 了一下 windbg  mutex. 有所收穫.
用這個帖子裏的方法: http://blogs.thinktecture.com/ingo/archive/2006/08/05/414674.aspx
可以找到是那個線程擁有了這個 mutex
運行 kd, 發現是恰好是另一個線程獲得了這個 mutex. 查看代碼, 發現在線程中有這樣的代碼:

 ...
 if (WaitForSingleObject(hmutex, 100) != WAIT_OBJECT_0) return FALSE;
 return TRUE;
  
如果返回 TRUE, 則認爲等到這個 mutex, 操作完成之後, 會調用 ReleaseMutex() 釋放這個 mutex. 如果返回 FALSE, 則不會調用 ReleaseMutex().
 
問題就出在這裏. 因爲線程可能被強制結束. 那麼就有可能沒有釋放 mutex. 這種情況下, MSDN 的描述是:
 
 If a thread terminates without releasing its ownership of a mutex object, the mutex object is considered to be abandoned. A waiting thread can acquire ownership of an abandoned mutex object, but the wait function will return WAIT_ABANDONED to indicate that the mutex object is abandoned.
 
下一個線程在等這個"被拋棄"的 mutex 時. WaitForSingleObject() 的返回值是 WAIT_ABANDONED, 而不是 WAIT_OBJECT_0. 而且, 這種情況下, 線程的確獲得了 mutex 的控制權. 但上面的代碼沒有考慮這種情況. 還是返回 FALSE,  導致後繼的 ReleaseMutex() 沒有調用. 因此導致另一個線程被死鎖.
 
幾點思考:
 1. 一定不要強制結束線程. 反覆強調這一點. 但程序員們還是照用不誤.
 2. 調用 API 的時候對於返回值的處理要很小心.

PS: 關於 Process Explorer 查看調用棧. 需要幾個前提:
 . 安裝最新的 Process Explorer
 . 安裝最新的 windbg
 . 設置好 _NT_SYMBOL_PATH, 比如:SRV*e:/symbols*http://msdl.microsoft.com/download/symbols
 . 使用命令 symchk /r %windir% 將符號下載到本地. 這個比較費時, 可能要下載超過 1G 的符號文件
 . 在 Process Explorer 菜單 Options/Configure Symbols 裏設置正確的 Dbghelp.dll 路徑以及 Symbols Path (默認是 _NT_SYMBOL_PATH 的值)
 
這些做好之後, 就可以在 Process Explorer 雙擊一個進程, 切換到 Threads 頁. 等初始化完成之後, 雙擊一個線程, 就可以看到調用棧了.  

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