出自一個unix鎖的問題解答
在unix上做C的開發(fā)已經(jīng)快2年了,一直在我們部門的一個主要產(chǎn)品項目組工作,該產(chǎn)品在大部分客戶那里一直穩(wěn)定的運行,沒有任何問題,而在少數(shù)幾個客戶那里,時不時的出現(xiàn)整個系統(tǒng)的吊死,而且該問題沒有任何規(guī)律可尋,除了系統(tǒng)吊死時候,我們對整個系統(tǒng)用pstack進行所有進程堆棧的跟蹤記錄外,我們沒有任何其它線索,沒有系統(tǒng)崩潰時候產(chǎn)生的CORE,我們開始面對來自客戶強大的壓力。
我開始被指派來解決這個問題。其實我們很早的就從進程的堆棧跟蹤里知道這個問題是死unix鎖問題,但是,它是怎么發(fā)生的那,我們幾乎排查了整個系統(tǒng)的代碼,但是一無所獲。
我們知道,當(dāng)主線程退出的時候,操作系統(tǒng)會直接終止該進程里面的其它線程,不會留任何機會讓那個線程做退出處理。而那個線程可能恰恰剛剛獲的了unix鎖,而還沒來得及解鎖,就被扼殺了,于是就造成了其它進程或線程再加這個unix鎖的時候,就會被阻塞,系統(tǒng)死鎖問題也就浮現(xiàn)了出來。
當(dāng)然,我知道了是競態(tài)條件問題造成了死unix鎖,就會通過pthread_join來同步等待那個線程退出,來消除這個競態(tài)條件,也就消除了死鎖,問題其實也就解決了。但是,我們有沒有辦法來消除由于一個unix鎖的owner沒有釋放該unix鎖,就死掉了,其它線程再加這個鎖的時候而造成的死鎖問題那?
關(guān)于上面這個問題,我曾經(jīng)請教過很多人,有的人想通過一個網(wǎng)絡(luò)unix鎖(就是加鎖、解鎖都要訪問網(wǎng)絡(luò)上的一臺機器)來解決,但是這些方法都不成熟,也很復(fù)雜,也好像有高手對這個問題不屑。我通過搜集很多資料發(fā)現(xiàn),原來解決這個問題非常的簡單,簡單的不能在簡單了。
在很多系統(tǒng)上,當(dāng)一個鎖的owner沒有釋放該unix鎖,就退出了,那么默認的方式就是其它線程再去加這個unix鎖的時候,就會阻塞,造成死鎖。而通過不同的屬性初始化這個鎖,我們能夠改變這種默認的方式:
- pthread_mutexattr_setprotocol(&mattr, PTHREAD_PRIO_INHERIT);
- pthread_mutexattr_setrobust_np(&mattr,PTHREAD_MUTEX_ROBUST_NP);
通過設(shè)置鎖的上面兩個屬性,我們就改變了默認的行為,當(dāng)一個unix鎖的owner死掉后,其它線程再去加這個鎖的時候,不會被阻塞,而是通過返回值EOWNERDEAD來報告錯誤,那么你可以根據(jù)這個錯誤來進行處理:首先是應(yīng)該調(diào)用pthread_mutex_consistent_np函數(shù)來恢復(fù)該鎖的一致性,然后調(diào)用解鎖pthread_mutex_unlock,接下來在調(diào)用加鎖,這樣該鎖的行為就恢復(fù)正常了。
如果上面這個函數(shù)在恢復(fù)unix鎖的一致性時候沒有成功,那么你只需要調(diào)用解鎖函數(shù)就OK了,然后直接返回,而不要去調(diào)用加鎖函數(shù),那么接下來的線程在調(diào)用加鎖函數(shù)的時候,會得到返回值ENOTRECOVERABLE,那么需要你做的就是調(diào)用pthread_mutex_destroy來destroy掉該鎖,然后重新用鎖的屬性和unix鎖的初始化函數(shù)來重新初始化該鎖。
上面的這些解決死unix鎖方式比較適合在系統(tǒng)中只有一個鎖的情況,如果系統(tǒng)的死鎖是由于多把鎖的資源互相等待而造成的,那么這種解決方式無能為力。
注:上面有一些函數(shù)后面有np后綴的,表示not portable,就是不可移值的意思,這些函數(shù)是一些系統(tǒng)自己實現(xiàn)的,而不是POSIX標(biāo)準(zhǔn)。
【編輯推薦】