在嵌入式系統(tǒng)中,如果使用基于優(yōu)先級調(diào)度算法的RTOS,系統(tǒng)中可能發(fā)生優(yōu)先級反轉(zhuǎn)現(xiàn)象。優(yōu)先級反轉(zhuǎn)用來描述系統(tǒng)中高優(yōu)先級任務(wù)由于等待低優(yōu)先級任務(wù)完成才能繼續(xù)執(zhí)行的情景,通常發(fā)生在試圖獲取信號量使用權(quán)或共享資源時。優(yōu)先級反轉(zhuǎn)可能會導(dǎo)致嚴重的后果。在小型嵌入式系統(tǒng)設(shè)計中,我們需要考慮如何訪問共享資源,避免資源競爭,防止優(yōu)先級反轉(zhuǎn)發(fā)生。
本文將介紹如何通過分析工具檢測OPENRTOS(FreeRTOS)應(yīng)用中的優(yōu)先級反轉(zhuǎn)現(xiàn)象,如何最小化優(yōu)先級反轉(zhuǎn)的影響,及如何在設(shè)計中避免優(yōu)先級反轉(zhuǎn)問題。
使用Tracealyzer工具檢測優(yōu)先級反轉(zhuǎn)現(xiàn)象
Tracealyzer是一個可視化分析工具,以圖形化的方式展示事件發(fā)生的序列。我們通過一個Percepio公司的示例說明如何使用Tracealyzer檢測優(yōu)先級反轉(zhuǎn)問題。應(yīng)用中存在一個隨機復(fù)位問題,在復(fù)位異常處理中設(shè)置斷點,發(fā)現(xiàn)該問題是由于看門狗定時器超時引起的,看門狗定時器應(yīng)該在一個周期運行的高優(yōu)先任務(wù)中重置。
插入自定義Tracealyzer用戶事件幫助工程師獲得更大的能見度?!坝脩羰录鳖愃朴诮?jīng)典的“printf()”調(diào)用。在此示例中,在看門狗計時器被重置或超時位置添加用戶事件。用戶事件還支持數(shù)據(jù)參數(shù),記錄重置之前的計時器值,以查看看門狗“margin”,即剩下的時間。捕獲結(jié)果如圖1所示。
圖1 用戶事件示例
從圖中我們可以看到SamplerTask任務(wù)正常運行,但任務(wù)在最后一個執(zhí)行過程中沒有清除看門狗計時器,導(dǎo)致看門狗重置事件發(fā)生。SamplerTask為什么沒有復(fù)位看門狗定時器?在Tracealyzer中使能Kernel Service calls,查看任務(wù)當(dāng)前在執(zhí)行什么操作,捕獲結(jié)果如圖2所示。
圖2 系統(tǒng)服務(wù)調(diào)用示例
SamplerTask任務(wù)的最后一個動作是調(diào)用OPENRTOS函數(shù)xQueueSend,將消息放入消息隊列中。注意,該標簽顯示為紅色,表示xQueueSend 函數(shù)將阻塞任務(wù),任務(wù)阻塞導(dǎo)致看門狗定時器復(fù)位之前,任務(wù)切換至ServerTask,從而導(dǎo)致看門狗超時,引起系統(tǒng)復(fù)位。
為什么xQueueSend阻塞了任務(wù)?雙擊此事件標簽,打開Object History視圖,顯示xQueueSend函數(shù)操作隊列“ControlQueue”的所有操作,如圖3所示。圖中最右邊一列展示了隊列存儲的消息??梢钥吹较㈥犃幸呀?jīng)包含五個消息,隊列已滿,因此xQueueSend操作引起任務(wù)阻塞。但ControlTask任務(wù)應(yīng)該讀取隊列數(shù)據(jù)并清空隊列緩沖區(qū),為什么預(yù)期的行為沒有發(fā)生?
圖3 Object history視圖
研究該問題,分析看門狗的margin如何隨時間而變化。這些信息記錄在User Event Logging中,使用User Event Signal Plot,我們可以繪制看門狗margin隨時間的變化。在同一時間軸上添加CPU負載圖,我們可以看到任務(wù)執(zhí)行如何影響看門狗margin,如圖4所示。
圖4 User event Single slot視圖
在CPU負載圖中,我們可以看到跟蹤的后半部分,ServerTask執(zhí)行消耗了約一半的跟蹤時間,這似乎影響了看門狗的margin。ServerTask任務(wù)(亮綠色)比ControlTask任務(wù)(深綠色)優(yōu)先級高,所以當(dāng)ServerTask任務(wù)消耗更多CPU時間后,ControlTask任務(wù)得到的CPU減少。由于高優(yōu)先級的ServerTask任務(wù)占用了許多CPU時間,導(dǎo)致ControlTask無法及時讀取消息隊列中的數(shù)據(jù)。這是優(yōu)先反轉(zhuǎn)問題的一個例子,SamplerTask任務(wù)被不相關(guān)的低優(yōu)先級任務(wù)阻塞。
解決方案
更改任務(wù)優(yōu)先級
一種解決辦法是改變優(yōu)先級,使ControlTask任務(wù)的優(yōu)先級高于ServerTask。圖5顯示了交換兩個任務(wù)調(diào)度優(yōu)先級后的運行結(jié)果。系統(tǒng)行為更穩(wěn)定。SamplerTask任務(wù)(紅色)的CPU負載穩(wěn)定在20%左右,其周期行為穩(wěn)定,看門狗margin為一條“線”,維持在10毫秒??撮T狗不會超時-問題解決了?。ㄗ⒁猓喝蝿?wù)由于優(yōu)先級改變,對應(yīng)的顯示顏色也發(fā)生了變化)。
圖5 交換ServerTask與ControlTask優(yōu)先級結(jié)果
使用互斥信號量
當(dāng)多個任務(wù)訪問同一資源時,例如網(wǎng)絡(luò)驅(qū)動或圖形顯示,可能會發(fā)生優(yōu)先級反轉(zhuǎn)。這種情況下,可以使用互斥信號量降低優(yōu)先級反轉(zhuǎn)的影響。
互斥信號量是包含優(yōu)先級繼承機制的二值信號量,二值信號量更適合實現(xiàn)任務(wù)間或任務(wù)與中斷服務(wù)之間的同步操作,互斥信號量更適用于互斥實現(xiàn)。
用作互斥機制時,互斥信號量相當(dāng)于一個令牌,保護共享資源。當(dāng)任務(wù)希望訪問該共享資源時,需要首先獲得(“take”)令牌。資源使用完后,任務(wù)必須釋放令牌,讓其它任務(wù)可以訪問相同的資源。
互斥信號量允許指定阻塞時間,阻塞時間指示一個任務(wù)在嘗試獲取令牌時,如果令牌不可用,任務(wù)可以阻塞的最大節(jié)拍數(shù)。然而,與二值信號量的區(qū)別在于,互斥量支持優(yōu)先級繼承機制。這意味著,如果有一個高優(yōu)先級的任務(wù)在試圖獲取當(dāng)前持有令牌的低優(yōu)先級任務(wù)占用的資源時,系統(tǒng)會臨時提升占有資源的任務(wù)優(yōu)先級別為被阻塞任務(wù)的優(yōu)先級。該機制確保高優(yōu)先級的阻塞時間盡量短,從而盡量減少優(yōu)先級反轉(zhuǎn)的時間。
優(yōu)先級繼承并不能解決優(yōu)先級反轉(zhuǎn)問題,它只是最小化了反轉(zhuǎn)在某些情況下產(chǎn)生的影響。硬實時應(yīng)用設(shè)計時,需保證優(yōu)先級反轉(zhuǎn)不會發(fā)生。
使用Gatekeeper任務(wù)
在訪問系統(tǒng)資源時,為了避免優(yōu)先級反轉(zhuǎn)問題,我們推薦使用“Gatekeeper”任務(wù)。Gatekeeper任務(wù)結(jié)構(gòu)如圖6所示,包含一個控制資源的任務(wù),接收數(shù)據(jù)/命令的隊列和一個回調(diào)函數(shù)。
應(yīng)用任務(wù)不直接訪問資源,而是將數(shù)據(jù)/命令寫入隊列。Gatekeeper任務(wù)處理接收到的數(shù)據(jù)/命令,并相應(yīng)地更新資源。資源狀態(tài)發(fā)生改變時,將觸發(fā)ISR (例如接收到一個新的網(wǎng)絡(luò)消息),將信息放置在隊列中,然后,Gatekeeper任務(wù)將執(zhí)行回調(diào)函數(shù),將新的數(shù)據(jù)傳給應(yīng)用任務(wù)。
此方法防止了系統(tǒng)資源訪問時優(yōu)先級反轉(zhuǎn)的出現(xiàn),是我們推薦的解決辦法。
圖6 Gatekeeper任務(wù)
結(jié)論
使用實時內(nèi)核,優(yōu)先級反轉(zhuǎn)問題是實時系統(tǒng)中出現(xiàn)得非常多的問題。在嵌入式系統(tǒng)系統(tǒng)設(shè)計時,應(yīng)盡量避免優(yōu)先級反轉(zhuǎn)發(fā)生,或者可以通過可視化分析工具捕獲該問題,通過相應(yīng)的RTOS方法降低其風(fēng)險。
審核編輯:湯梓紅
評論