陽 麗
核間自旋鎖的使用和問題定位方法
陽 麗
(萍鄉(xiāng)學院 信息與計算機工程學院,江西 萍鄉(xiāng) 337000)
多核CPU在不同的內(nèi)核間同步,通常采用核間自旋鎖的方法來保證多核之間互斥。核間自旋鎖的使用頻度需要根據(jù)CPU的核處理能力和用戶程序?qū)PU總體處理能力的要求來進行權(quán)衡。如果核間自旋鎖的使用過于頻繁或核上加鎖周期過長,就會導致CPU的單位時間資源使用率過高,使用戶代碼功能的執(zhí)行效率降低。為解決核間自旋鎖在使用過程中遇到的問題,文章提出的解決方案是在為核間自旋鎖加鎖和解鎖過程增加相應的調(diào)試信息,然后針對不同情況進行掃描隊列或新增核間自旋鎖。
核間自旋鎖;多核CPU;核處理能力
核間自旋鎖是為了實現(xiàn)保護共享資源而提出的一種鎖機制。由于用戶程序在多核CPU上執(zhí)行時,有些需要在各個核之間進行數(shù)據(jù)的交互,并且數(shù)據(jù)交互的方式是使用核間共享內(nèi)存的方式實現(xiàn)的,因此被讀寫的核間共享內(nèi)存就成為了臨界資源,不允許有其他的任務訪問該資源。對臨界資源互斥訪問最常見的方法有使用互斥信號量或自旋鎖[1]。
信號量是一種睡眠鎖,如果有一個任務試圖獲得一個已經(jīng)被占用的信號量時,信號量就會將其推進到一個等待隊列中,然后讓其睡眠。當持有信號量的進程將信號量釋放后,處于等待隊列中的那個任務將被喚醒,并獲得該信號量。而一個自旋鎖就是一個互斥設備,它只能有兩個值:“鎖定”和“解鎖”[2]。如果自旋鎖可用,則“鎖定”位被設置,而代碼繼續(xù)進入臨界區(qū);相反,如果自旋鎖被其他進程爭用,則代碼進入忙循環(huán)并重復檢查這個鎖,直到該自旋鎖可用為止,這個循環(huán)就是自旋鎖的“自旋”[3]。一個被爭用的自旋鎖使得請求它的線程在等待自旋鎖重新可用時自旋,因此特別浪費處理器資源,自旋鎖不應該被長時間持有,該鎖比較適用于鎖使用者保持鎖時間比較短的情況[4]。但是信號量機制的獨有睡眠—喚醒過程比自旋鎖有更大的時間開銷,可以應用于臨界區(qū)大的情況,自旋鎖的效率遠高于信號量機制。
本文針對自旋鎖在使用過程中遇到的問題提出了一種為防止核間自旋鎖死鎖的故障定位和解決方案。
在用戶多任務程設計中,為了減少系統(tǒng)內(nèi)存碎片和降低內(nèi)存申請的時間開銷,共享內(nèi)存中的一塊區(qū)域被規(guī)劃成UB池,其作用就是為了實現(xiàn)對頻繁申請內(nèi)存的高效的管理為,可以很大程度地減少內(nèi)存碎片,同時也能縮減申請時間。實時的任務使用這種方法相當?shù)母咝5],其中有一塊UB池用于核間使用。因此獲取UB和釋放UB的過程中都會涉及臨界資源的操作,影響共享內(nèi)存的讀寫操作。
根據(jù)上文描述的臨界資源保護機制可知,使用互斥信號量會到導致等待鎖的任務休眠,而自旋鎖是自旋等待。因此,對于帶有門鈴中斷DoorBell并要求處理時間比較短的用戶程序而言,使用自旋鎖將會是最好的選擇[3],而且自旋鎖會盡可能地減少線程的阻塞。
使用自旋鎖也會導致新的問題,自旋鎖最多只能被一個可執(zhí)行的線程持有,如果一個執(zhí)行線程試圖獲得一個被爭用的自旋鎖,那么該線程就會一直進行忙循環(huán)—旋轉(zhuǎn)—等待鎖重新可用[6]。當系統(tǒng)進行測試使用自旋鎖來實現(xiàn)對核間共享UB的互斥進行保護時,如果不能準確地控制其自旋鎖進行保護,各個關(guān)聯(lián)CPU的核將會進入死鎖狀態(tài),并且系統(tǒng)不能提供任何異常打印。如果鎖的競爭激烈,或者持有鎖的線程需要長時間占用鎖執(zhí)行同步塊,這時候就不適合使用自旋鎖了,因為自旋鎖在獲取鎖前一直都是占用CPU做無用功,同時有大量線程在競爭一個鎖,導致獲取鎖的時間很長,線程自旋的消耗大于線程阻塞掛起操作的消耗,其他需要CPU的線程又不能獲取到CPU,造成CPU的浪費,這種情況下需要關(guān)閉自旋鎖[7]。
針對前文描述情況,假設已知用戶程序故障的問題是由于增加了核間自旋鎖后導致的,省略了其他問題導致的定位過程,下文將主要描述如何定位自旋鎖導致核間死鎖的原因。在定位核間死鎖前,首先需要在加鎖和解鎖函數(shù)中增加調(diào)試信息。調(diào)式信息結(jié)構(gòu)體為:
typedef struct
{
WORD32 index;
/*調(diào)試信息次數(shù)*/
WORD32 flag;
/*加解鎖標志,0-unlock;1-lock*/
WORD32 line;
/*調(diào)用加解鎖函數(shù)的行號*/
WORD32 isSucc
;/*加解鎖是否成功標志,0-succ;1-fail*/
Char func[32];
/*調(diào)用加解鎖的函數(shù)名*/
}LockLog;
添加系統(tǒng)自動記錄該自旋鎖的詳細使用過程的功能,當死鎖出現(xiàn)后,自動停止記錄,保留現(xiàn)場,手工解鎖后將記錄導出進行分析。當出現(xiàn)死鎖場景時,如果每次導出的死鎖現(xiàn)場記錄都是一致的,可以根據(jù)情況分析,典型的錯誤打印如下例LOG輸出所示:
I = 22, index = 6789022, func = DoorBellDataSend, line = 8572, action = 1, result = 0
I = 23, index = 6789023, func = DoorBellDataSend, line = 8572, action = 0, result = 0
I = 24, index = 6789024, func = DoorBellDataSend, line = 8572, action = 1, result = 0
I = 25, index = 6789025, func = ScanAndDispatchMsg, line = 4392, action = 0, result = 0
I = 26, index = 6789026, func = DoorBellDataSend, line = 8572, action = 1, result = 0
I = 27, index = 6789027, func = DoorBellDataSend, line = 8572, action = 0, result = 0
I = 28, index = 6789028, func = Macshd_writeMsg, line=15703, action=1, result=0
I = 29, index = 6789029, func = Macshd_writeMsg, line=15703, action=0, result=0
I = 30, index = 6789030, func = ReadRng, line = 6772, action = 1, result = 0
I = 31, index = 6789031, func = DoorBellDataSend, line = 8572, action = 1, result = 1
I = 32, index = 6789032, func = OSSSendMsg, line = 3614, action = 1, result = 1
此LOG記錄了系統(tǒng)對公共UB自旋鎖的使用過程,其中I表示本次記錄在內(nèi)存中的位置;index表示操作鎖的順序號;func表示觸發(fā)本次操作的函數(shù);line表示代碼所處的行號;action表示動作,0表示解鎖動作,1表示加鎖動作;result表示action的結(jié)果,0表示成功,1表示失敗。
按執(zhí)行次序可以得到如下初步結(jié)論:
(1)第index=6789030次之前對鎖的操作都是正常的,所有的加鎖與解鎖按順序進行且正常返回。
(2)第index=6789030次,用戶程序ReadRng()函數(shù)加鎖成功,但是第index=6789031次不是這個ReadRng()函數(shù)的解鎖動作,而是一次未成功DoorBellDataSend()函數(shù)的加鎖動作。
(3)接著第index=6789032次OSSSendMsg()函數(shù)也嘗試加鎖,結(jié)果也沒有成功。
至此,可以判斷系統(tǒng)已經(jīng)處于死鎖狀態(tài)。
深入分析最后三次自旋鎖的操作,發(fā)現(xiàn)死鎖跟最后一次序號為index=6789032的操作沒有直接的聯(lián)系,死鎖主要是由第index=6789030次和第index=6789031次操作造成的,下面結(jié)合函數(shù)功能進行詳細說明。
ReadRng()是一個處理普通用戶任務的函數(shù),該函數(shù)存放在處理器CPU-5中,它的功能是用來處理DSP發(fā)送過來的消息內(nèi)容;而DoorBellDataSend()函數(shù)是位于DSP傳送過來的DOORBELL()函數(shù)的中斷處理程序中,優(yōu)先級更高,也是在處理器CPU-5中運行。由于ReadRng()函數(shù)和DoorBellDataSend()函數(shù)都需要運行CPU-5,所以當前者獲取了自旋鎖時,后者又嘗試獲得該自旋鎖,就得不到該自旋鎖[8]。而自旋鎖的一個特性就是當同一個CPU核心連續(xù)兩次獲得同一個自旋鎖的時候會造成死鎖,這種死鎖方式被稱為自死鎖,也是最常見的自旋鎖死鎖形式。
最后一條記錄中的OSSSendMsg()函數(shù)的功能是組織消息內(nèi)容發(fā)送給DPS,發(fā)送過程中雖然有加鎖動作,但因為該任務是在CPU-8內(nèi)核中運行,具有獨立性,所以假如該自旋鎖被CPU-5內(nèi)核釋放,CPU-8內(nèi)核的死鎖是可以馬上恢復的。
根據(jù)自旋鎖的特點和以上的故障分析,要解決這個死鎖問題,則必須要求在任何一個CPU核上都不能陷入自死鎖。當前示例中的程序使用了三個CPU核,其中除了CPU-5可能會存在上文描述的這種中斷導致自死鎖的情況,其他兩個CPU核都不會存在鎖定被中斷的場景,因此只要解決CPU-5上產(chǎn)生的上述問題就可以了。
死鎖的處理策略主要包括死鎖的預防、死鎖的避免、死鎖的檢測和解除三種。
死鎖的預防是通過設置某些限制條件,去破壞產(chǎn)生死鎖的四個必要條件(互斥、請求與保持、不搶占、循環(huán)等待)中的一個或多個條件,來預防死鎖的發(fā)生。這種方法容易實現(xiàn)從而被廣泛使用,但由于所實施的限制條件往往過于嚴格,因而可能導致系統(tǒng)資源利用率和吞吐量降低。
避免死鎖是在資源的動態(tài)分配過程中,采用某種方法去防止系統(tǒng)進入不安全狀態(tài),從而避免死鎖,而不需要事先采取各種限制措施去破壞產(chǎn)生死鎖的四個必要條件。這種方法施加的限制條件較弱,并且有一定的專用性。
死鎖的檢測與解除是指當檢測到系統(tǒng)中已發(fā)生死鎖時,需要將進程從死鎖狀態(tài)中解脫出來,常用的實施方法是撤銷或掛起一些進程,以便回收一些資源,再將這些資源分配給已處于阻塞狀態(tài)的進程,使之轉(zhuǎn)換為就緒狀態(tài)繼續(xù)運行。死鎖的檢測和解除措施,有可能使系統(tǒng)獲得較好的資源利用率和吞吐量,但在實現(xiàn)上難度非常大[9]。因此,在考慮到實現(xiàn)成本后,本文將從死鎖的預防和避免入手提出相應的通用解決方案。
此方法首先要修改用戶程序的狀態(tài)機邏輯,避免在中斷服務程序中使用自旋鎖,接著創(chuàng)建一個新的隊列,保存要執(zhí)行的任務,并加入結(jié)點的鎖定狀態(tài)。然后開啟一個單獨CPU核運行一個掃描隊列的任務,當DOORBELL中斷到來時,中斷服務程序僅執(zhí)行寫隊列操作,接著通知對應的CPU核去處理DOORBELL中斷。與此同時,中斷服務程序檢測前驅(qū)結(jié)點的鎖定狀態(tài),如果鎖定狀態(tài)為False,則說明該線程是隊列中的第一個線程;如果鎖定狀態(tài)為True,則說明該線程前面已經(jīng)有線程獲取了自旋鎖,當前線程需要阻塞自旋,然后通知對應的CPU-5核取處理。
隊列鎖的優(yōu)點是設計空間復雜度低(如果有n個線程,L個鎖,每個線程每次只獲取一個鎖,那么需要的存儲空間是O=(L+n)。其次隊列鎖能對自旋鎖進行優(yōu)化,因為隊列鎖中每次只有一個線程與外部的線程進行爭搶,一次爭搶不會產(chǎn)生大量失敗,同時失敗的線程會進入到隊列休眠,等待前驅(qū)節(jié)點將其喚醒,這樣提高了CPU的利用率。由于隊列內(nèi)每次只有一個線程會爭搶自旋鎖,因此每次最多觸發(fā)一次cache失效。
雖然不提倡中斷服務程序中使用自旋鎖,但仍然有設計者需要實現(xiàn)某些特定需求的用戶程序,只能在中斷服務程序中使用自旋鎖,這時通用的方法就不能解決該問題了。
針對中斷情況的解決方法是避免帶有中斷的任務獲取到自旋鎖,根據(jù)上文中的程序案例特點,另外分配一塊公共UB池,對應該UB池再加一個新的自旋鎖進行保護。采取這種方法后DOORBELL()函數(shù)就不會在前驅(qū)結(jié)點的鎖定域上等待,而是在自身結(jié)點的鎖定域上等待,從而不會出現(xiàn)如上文中CPU-5連續(xù)兩次獲得同一自旋鎖的情況,它將會在其他CPU上完成。
另外,該UB池在DOORBELL()函數(shù)處理時進行申請,僅用于DOORBELL()函數(shù)觸發(fā)的UB申請,網(wǎng)絡消息處理觸發(fā)的UB申請流程還是沿用之前的機制,而釋放UB池則放在OSSSendMsg()函數(shù)即CPU-2核上執(zhí)行。因為即使CPU-5被占用,這時DOORBELL()函數(shù)中斷到來,CPU-5由于獲取不到自旋鎖而進入停等狀態(tài)。但是當CPU-2釋放完UB池時,占用自旋鎖的線程會釋放該自旋鎖,因此CPU-5會馬上恢復[10]。雖然根據(jù)用戶程序的健壯性可能會出現(xiàn)功能上的錯誤,但不會出現(xiàn)導致系統(tǒng)死機的情況。而這個方法的優(yōu)點在于對原有邏輯的改動較少,并且保留了用戶程序中必要的中斷處理邏輯。
目前為了提高硬件的處理能力,用戶往往選用基于多核的CPU。多核的CPU可以同時運行多項任務,但任務之間往往需要具有一定的同步關(guān)系,以及多個并行任務對同一資源的操作也需要使用信號量或自旋鎖來解決。信號量和自旋鎖的使用經(jīng)常會帶來執(zhí)行效率降低和死鎖問題。本文對于防止核間自旋鎖的死鎖提供了一種定位和解決方法。使用本文描述的通用解決方案對系統(tǒng)改動最小,并可以滿足一般用戶的使用要求。針對中斷任務,雖然不能完全解決死鎖問題,但在保證系統(tǒng)健壯性的同時,減少了用戶對程序的改動,保證了用戶程序的正常邏輯執(zhí)行。
[1] 李娟, 任曉瑞. 一種機載嵌入式對稱多處理機系統(tǒng)互斥策略[J]. 電子科技, 2013, 26(4): 60–64.
[2] 許璐璐. 支持對稱多核處理器的嵌入式實時操作系統(tǒng)研究與實現(xiàn)[D]. 北京: 中國航天科技集團公司第一研究院, 2016.
[3] 李天佑. 基于三層特權(quán)級的操作系統(tǒng)安全體系結(jié)構(gòu)的研究[D]. 北京: 北京交通大學, 2014.
[4] William S. 操作系統(tǒng)精髓與設計原理: 第3版[M]. 北京:清華大學出版社, 1998: 213.
[5] 虞保忠, 郝繼鋒. 多核操作系統(tǒng)自旋鎖技術(shù)研究[J]. 航空計算技術(shù), 2017, 47(4): 36–40.
[6] 王月, 李杰.嵌入式多核系統(tǒng)的可調(diào)優(yōu)先級自旋鎖[J]. 單片機與嵌入式系統(tǒng)應用, 2021, 21(12):46–51
[7] 呂錦柏, 崔萍. 一種多核CPU訪問資源時自旋鎖的實現(xiàn)方法: CN201710376711.2[P]. 2017-05-25.
[8] 李亞爽, 姬希娜, 王振, 等. Nucleus PLUS自旋鎖測試方法研究[J]. 電子技術(shù)應用, 2018, 44(1): 12–16
[9] 于璠, 王振國. 一種自旋鎖搶占調(diào)度算法選擇方法及裝置: CN201310705505.3[P]. 2013-12-19.
[10] 張文盛, 侯整風. 一種Linux內(nèi)核自旋鎖死鎖檢測機制的設計與實現(xiàn)[J]. 合肥學院學報, 2012, 22(2): 56–60.
The Use of Inter-Core Spin Locks and Problem Location Methods
YANG Li
(School of Information and Computer Engineering, Pingxiang University, Ping Xiang, Jiangxi 337000, China)
In different inter-core synchronization technologies in multi-core CPU, the inter-core spin lock method is often inevitably used. The frequency of the usage of inter-core spin locks needs to be weighed according to the CPU’s core processing capability and the user program’s requirements on the CPU’s overall processing capability. If the inter-core spin lock is used too frequently or the lock period on the core is too long, the CPU’s resource utilization per unit time will be too high and the execution efficiency of user code functions will be reduced. For this reason, the paper proposed to add corresponding debugging information to the inter-core spin locking and unlocking process, and to use the solution of scanning queue or adding inter-core spinlock for different situations.
inter-core spin lock; multicore CPU; core processing power
TP316
A
2095-9249(2021)06-0073-04
2021-10-26
陽麗(1980—),女,江西萍鄉(xiāng)人,講師,碩士,研究方向:計算機應用、軟件工程、數(shù)據(jù)庫。
〔責任編校:吳侃民〕