死鎖可以稱為進程死鎖。那么是在多進程(並發)情況下可能會出現的。
指的是多個進程因為競爭資源而造成的僵局(互相等待),沒有外力,那么所有進程都會無法向前推進。
所以是在操作系統和並發程序設計中需要特別考慮的問題。
因此,可以可以得出如下的場景和必備條件。
場景:
- 系統資源的競爭。只有資源不足時才會出現死鎖可能,另外,可剝奪資源的競爭是不會引發死鎖的;
- 進程推進順序不對。多進程在運行時,請求和釋放資源的順序不當。
- 系統資源分配不當。
四大必要條件:
- 互斥:進程對分配到的資源排它性使用。獨占資源,是由資源本身的屬性決定的。
- 請求和保持:保持已有資源,同時請求新的資源,在請求過程中以及因為沒有得到新資源而阻塞,已有資源仍然保持;
- 不可剝奪:進程已有的資源在使用完之前不能被剝奪,只能自己釋放;
- 環路等待:必然存在一個進程資源環形請求鏈。
死鎖預防:打破之前四個條件
- 打破互斥在實際中應用不大;
- 打破請求與保持,可以實行資源預先分配策略,即進程在運行前一次性申請所需要的全部資源,如果不能滿足,則暫不運行。實際應用中,進程在執行時是動態的,不可預測的,並且資源利用率低,降低了進程並發性。
- 打破不可剝奪,當請求新資源不能滿足,需要釋放已有資源,系統性能受到很大降低
- 打破循環等待:實行資源有序分配策略。可以將資源事先分類編號,按號分配,使進程在申請、占用資源是不會形成環路。所有進程對資源的請求必須嚴格按資源序號遞增的順序提出。但是也有問題,合理編號困難,增大系統開銷,另外也增加了進程對資源的占有時間。
死鎖避免:
不限制進程有關申請資源的命令,而是對進程所發出的每一個申請資源命令加以動態地檢查,並根據檢查結果決定是否進行資源分配。就是說,在資源分配過程中若預測有發生死鎖的可能性,則加以避免。這種方法的關鍵是確定資源分配的安全性。
銀行家算法(1968年):允許進程動態地申請資源,系統在每次實施資源分配之前,先計算資源分配的安全性,若此次資源分配安全(即資源分配后,系統能按某種順序來為每個進程分配其所需的資源,直至最大需求,使每個進程都可以順利地完成),便將資源分配給進程,否則不分配資源,讓進程等待。
死鎖檢測與修復:
預防和避免的手段達到排除死鎖的目的是很困難的。一種簡便的方法是系統為進程分配資源時,不采取任何限制性措施,但是提供了檢測和解脫死鎖的手段:能發現死鎖並從死鎖狀態中恢復出來。因此,在實際的操作系統中往往采用死鎖的檢測與恢復方法來排除死鎖。
----------------------------------------------------------------------------------------------------
死鎖常見的場景如下:
忘記釋放鎖:
void data_process()
{
EnterCriticalSection();
if(/* error happens, forget LeaveCriticalSection */)
return;
LeaveCriticalSection();
}
單線程重復申請鎖(所以單線程的時候也有可能進入死鎖)
void sub_func()
{
EnterCriticalSection();
do_something();
LeaveCriticalSection();
}
void data_process()
{
EnterCriticalSection();
sub_func();
LeaveCriticalSection();
}
多線程多鎖申請
void data_process1()
{
EnterCriticalSection(&cs1); // 申請鎖的順序有依賴
EnterCriticalSection(&cs2);
do_something1();
LeaveCriticalSection(&cs2);
LeaveCriticalSection(&cs1);
}
void data_process2()
{
EnterCriticalSection(&cs2); // 申請鎖的順序有依賴
EnterCriticalSection(&cs1);
do_something2();
LeaveCriticalSection(&cs1);
LeaveCriticalSection(&cs2);
}
多線程環形鎖
編程中如何來避免:
- 檢查有沒有忘記釋放鎖
- 如果自己模塊可能重復使用一個鎖,建議使用嵌套鎖
- 建議使用庫里面的鎖
- 如果某項業務需要獲取多個鎖,保證鎖按某種順序來獲取
- 編寫簡單測試用例驗證是否存在死鎖
參考:https://www.cnblogs.com/kuliuheng/p/4071555.html
