SPIN LOCK死鎖問題

2021-07-25 05:07:25 字數 3102 閱讀 8034

板卡公升級版本後,初始化疑似未執行完,ssh無法連線,可以ping通。

通過檢視控制台的列印,可以看到,初始化執行到了57一半,就卡住了。

兩次控制台輸入i(這個命令是我們內部實現的,功能和vxworks的類似,列印一些任務狀態),只有pid為1785的程式執行時間在增長,懷疑任務陷入死迴圈。控制台檢視其堆疊:

[<0x2b55ad04>] (pthread_spin_lock + 0x4)
該任務在持續獲取一把自旋鎖。通過在正常版本檢視,優先順序為16的實時任務為logtask,負責向控制台輸出列印。pid為1782的應該為主執行緒,負責執行初始化,檢視其堆疊如下:

tt 1782

[<0x2b55ad4c>] (pthread_spin_trylock + 0x1c)

value = 2 = 0x2

至此,懷疑logtask任務以及主線程任務,由於spin lock,互相鎖死。檢視bsp**,用到的自旋鎖變數為g_logmsgbuf_sl,檢視其內容:

g_logmsgbuf_sl

g_logmsgbuf_sl =0x837078: value = 1 = 0x1

可以看到,其值為1,說明已經有人拿到這把鎖。由於兩個任務優先順序不同,主線程為非實時優先順序,logtask為實時優先順序,懷疑主線程在拿到鎖後,被logtask打斷,而logtask又無法獲取該鎖,造成死鎖。

通過在控制台使用chrt命令,將logtask任務的排程策略修改為other:

[2016/07/25 10:41:04]:->system "chrt -o -p 0 1785"

[2016/07/25 10:41:14]: pid 1785's current scheduling policy: sched_fifo

[2016/07/25 10:41:14]: pid 1785's current scheduling priority: 16

[2016/07/25 10:41:14]: pid 1785's new scheduling policy: sched_other

[2016/07/25 10:41:14]: pid 1785's new scheduling priority: 0

[2016/07/25 10:41:14]: value = 0 = 0x0

之後,控制台列印恢復,開始列印後面的初始化資訊。至此,原因已基本分析清楚,需要從**層面,進行理論分析。

問題分析

首先分析logmsg的實現,偽**如下:

void logmsg(fmt,…)

else

}

在該函式內,只是嘗試獲取鎖,並沒有死等該資源,這是因為這個介面幾乎所有任務都會呼叫,不能因為一條列印就阻塞任務的執行,如果獲取不到鎖則直接丟棄列印。也可以從這段**看出,這把鎖主要是用來做緩衝區互斥的。下面分析logtask任務的實現:

void logtask()

}}

logtask阻塞在sem上,等待logmsg通知後,將緩衝區的所有資料全部輸出,當沒有資料可輸出時,再次返回阻塞在sem上。

分析了一種可能導致死鎖的情況:

(1) 有資料輸出,logtask取到資料,解鎖spin,然後發生任務切換

(2) 主線程拿到cpu,並且需要輸出列印,成功獲取到spin lock,並且正在將資料放入緩衝區

(3) 此時logtask執行完printf,由於優先順序高於主線程,因此搶占cpu,嘗試獲取spin lock,但是由於spin lock在主線程,因此logtask會處於一直嘗試獲取spin lock中,而這造成了主線程無法獲取到cpu,所以無法進一步釋放spin lock,最終導致死鎖

解決方案

 調整主線程任務優先順序,必須保證所有任務優先順序都比logtask高,因為整個系統中只有logtask使用的是spin_lock,其他任務在logmsg中使用的是trylock,高優先順序trylock時,並不會造成死鎖。

 spin lock是乙個比較危險的鎖,在網上查了一些資料,除非對效能極其在意,均不建議使用spin lock。bsp修改**,將spin lock修改為mutex。spin lock與mutex效率對比見後。

在smc對比了二者時間。測試程式迴圈執行lock與unlock,不考慮鎖衝突的情況,在板卡上執行10000次,結果如下:

spin lock: 0 s, 516027 ns

mutex: 0 s, 1596182 ns

從效率來說,spin lock比mutex大概快3倍。從網上查閱了一些資料,spin lock的實現無需切換至核心態,在使用者態通過彙編直接實現,實際上是對乙個變數進行原子加減。而mutex需要切換至核心態。

在問題分析時,實際上是有乙個問題沒有解釋清楚的,當時分析的可能的死鎖情況的第一步:

(1) 有資料輸出,logtask取到資料,解鎖spin,然後發生任務切換

這塊是說不通的,在高優先順序解鎖spin後,低優先順序是如何獲取到cpu,搶到spin,導致在高優先順序拿不到spin?在logtask中,唯一可能引起阻塞的操作就是最外層的sem_wait,否則在內層迴圈中,logtask應該持續占用cpu才對,低優先順序的主線程又是如何搶到cpu的?

針對這個問題,編寫測試**如下:

void func()

pthread_spin_unlock(&sp);

}}int main()

在上述**中已遮蔽掉不重要的函式。在main函式中,建立乙個優先順序為16的實時任務,該任務不停在獲取、釋放spin lock。而在main函式建立執行緒後,將會嘗試去獲取spin lock。一旦主線程獲取到這把鎖,則高優先順序任務中trylock就會失敗,並產生列印。在實際測試中,發現低優先順序的主線程確實會在比較長的一段時間(大概幾十秒)後,拿到這把鎖。拿到鎖後,螢幕會大量出現pthread_spin_trylock failed的列印,但是主線程依然沒有退出。

目前,我們能得到的結論是,高優先順序任務佔據了極大比例的cpu,但是當前的任務排程並沒***完全的占用,低優先順序任務依然有極低的機率,占用了極低的一部分cpu(pthread_spin_lock效率極高,但是程序退出就沒有執行完成)。

Linux 死鎖問題

之前的部落格都多次提到了死鎖問題,那麼我們先來了解一下。什麼是死鎖?其實死鎖是指在多道程式系統中,一組程序中的每乙個程序均無期限的等待被該組程序中的另乙個程序所占有且永遠不會釋放的資源,執行緒一樣。這種現象稱系統處於死鎖狀態,簡稱死鎖。處於死鎖狀態的程序稱為死鎖程序。產生死鎖的四個必要條件 其實死鎖...

php死鎖問題

背景 對於死鎖的問題,人們往往想到出現一些關於訪問很緩慢,有白頁現象,要是測試環境 我就真實遇到測試環境有本文談及一樣的問題 你也就重啟一下php的php fpm程序發現又好了,隔一段時間又出類似的問題,你會看下日誌,你會發現有很多日誌是 max execution timeout of 60 se...

執行緒死鎖問題

所謂死鎖是指多個執行緒因競爭資源而造成的一種僵局,多個執行緒被無限的阻塞,執行緒之間相互等待所需的資源,若無外力作用,這些程序都將無法向前推進。死鎖的產生,通常是下面的兩種情況 1.如果執行緒試圖對同乙個互斥量加鎖兩次,那麼它自身就會陷入死鎖狀態,使用互斥量時,2.如果程式使用多個互斥量時,如果允許...