我在馬路上遇到一個死鎖問題
掃描二維碼
隨時隨地手機看文章
▍馬路上的死鎖問題
我在市里的老區(qū)馬路上遇到堵車了,動彈不得,后來發(fā)現(xiàn)是個“死鎖”問題。
當時我坐在③號車里,等了好久,忽然職業(yè)病犯了,好想下車跟他們講解下發(fā)生了什么,“死鎖”是怎么造成的,如何避免……
簡要情況,見下圖,原因是右上方的小路一次只能通過一個方向的車,大路上紅色的車可能因為沒找到車位,隨便停路邊了,導致圖中①、②、③、④、⑤號車動彈不得。
實際上,在擁堵的馬路,如果沒有交通指示的話,很容易造成類似這種“死鎖”。如下圖這種情形。
除了交通上這個案例,我們再來看看網(wǎng)絡上的一張趣圖(姑且叫人質問題,圖來源于https://stackoverflow.com/questions/34512/what-is-a-deadlock叫Criminal & Cop Scene):
扯遠了,我們還是認真地聊聊這個“死鎖”問題吧。
▍鎖的概念
鎖是什么?其實概念很簡單,就是平時我們見到的鎖。只是操作系統(tǒng)仿照現(xiàn)實上的這個概念做了一套邏輯而已。原理,我還是可以從現(xiàn)實生活中去理解的。
例如,你去買衣服,店里有試衣間。當你想試一下衣服的時候,就可能要用到試衣間,你進了試衣間,就要將門鎖上,表示你當時占用試衣間這個資源,用完就釋放,然后別人就可以用了。試想下,如果你不鎖門,代價可能就是春光乍泄了……
如果你買衣服從沒進過試衣間,那就自己腦補下上公共廁所的例子吧……
回到軟件上的鎖概念,一般OS會提供兩種接口來訪問這個鎖,一個是Acquire或者叫Require/Get/Wait,一個Release,即表示占有并鎖上資源和釋放這個資源。
一般RTOS會用Semaphore或者Mutex來實現(xiàn)這個鎖的功能,當然,復雜的系統(tǒng)如Linux會有更多種類的鎖,如自旋鎖,互斥鎖,讀寫鎖等等。
▍鎖的作用
這還用說嗎,目的只有一個,你占用的時候,不想被被人占有。防止各種各樣的問題。例如,上廁所記得把門鎖上,不然別人推開了,你會罵人家流氓,或者,人家也罵你流氓,上廁所不鎖門……
還有哦,算了,程序員有女朋友嗎?有的話,差不多就要去扯證,長期官方鎖上……哈哈哈
▍死鎖的原因
情景1:嵌套鎖/遞歸鎖的使用
上段代碼來看看:
task1在第一個Doing someting中被task2搶占,死鎖就開始了。這是一種嵌套鎖使用遇到的死鎖情形。遞歸鎖(如果你真用過,那真有裝逼的嫌疑了),也會有這樣的問題。
現(xiàn)實項目中,可能會更隱秘,找到它需要花點心思。
情景2:鎖還沒被釋放,Task卻被kill了
RTOS設計的沒那么智能,task被kill,其所占有的資源不一定會被自動釋放。這種情形,很容易發(fā)生在關機的流程。設計上一定要非常謹慎。
情景3:Task給自己發(fā)mailbox
Tasks之間的通信,我們一般都是用mailbox,同一個task的mailbox接口可以被多個其他task調用,也不會發(fā)生資源沖突問題。實際上,mailbox內也隱藏著一個鎖的功能,一個task調用了mailbox接口,其他task只能等它調用完了,才能調用。
一般定義的mailbox是一個空間有限的隊列,在頻繁調用的時候,資源不夠,不讓正在調用的task在等。這實際上很正常,不正常的時候,如果task自己調用了自己的mailbox接口,那就容易見鬼了。
假如上圖的task1優(yōu)先級很低,定義的mailbox大小為4,都被task2、task3、task4、task5被調用了,而task1還沒來得及處理釋放mailbox資源。正在此時task1的某些應用代碼也需要發(fā)一個消息,調用了自己的mailbox接口,然后就死了……
▍死鎖的避免
使用鎖的注意事項,不詳細解釋了,總結以下幾條:
不允許使用嵌套鎖,即不要用鎖中鎖;
不允許使用遞歸鎖,這個跟嵌套鎖類似;
不要在中斷服務中使用鎖;
Task或者Thread被kill之前,確保其占有的鎖已經釋放了;
Require資源的時候,盡量不用死等(即超時為Forever)。
-END-
本文授權轉載自公眾號“嵌入式軟件實戰(zhàn)派”,作者:實戰(zhàn)派大師兄
推薦閱讀
免責聲明:本文內容由21ic獲得授權后發(fā)布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!