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