葉柯陽 王宜懷 徐婷婷 劉長勇
1(蘇州大學(xué)計算機科學(xué)與技術(shù)學(xué)院 江蘇 蘇州 215006) 2(武夷學(xué)院數(shù)學(xué)與計算機學(xué)院 福建 武夷山 354300)
mbedOS是一款面向ARM Cortex-M系列處理器的開源實時操作系統(tǒng)(Real-Time Operating System,RTOS)[1]。它于2014年由ARM公司推出,專為物聯(lián)網(wǎng) (Internet of Things,IoT) 中的“物體”設(shè)計[2],包含了開發(fā)基于Arm Cortex-M微控制器的聯(lián)網(wǎng)產(chǎn)品所需的所有特性,包括安全性、連接性、RTOS傳感器和I/O設(shè)備的驅(qū)動程序,具有堆棧共用和事件驅(qū)動兩大代表性特點。
目前,針對嵌入式實時操作系統(tǒng)優(yōu)先級反轉(zhuǎn)問題的研究集中在VxWorks操作系統(tǒng)、WinCE操作系統(tǒng)、μC/OS-Ⅱ操作系統(tǒng)等,而對mbedOS實時操作系統(tǒng)避免優(yōu)先級反轉(zhuǎn)問題的機制研究方面缺乏相關(guān)材料。為此,本文將利用Cortex-M4內(nèi)核的STM32微控制器,基于Kinetis Design Studio 3.1.0 IDE開發(fā)環(huán)境和SD-mbedOS工程框架[3],通過一個測試工程對mbedOS避免優(yōu)先級反轉(zhuǎn)問題的機制進(jìn)行分析,剖析其從各線程啟動,到申請互斥量,再到最終釋放互斥量的全過程,結(jié)合相關(guān)函數(shù)、關(guān)鍵代碼、流程圖、時序圖等分析其實現(xiàn)的原理。通過對mbedOS避免優(yōu)先級反轉(zhuǎn)問題機制的剖析,可為 mbedOS的應(yīng)用研究和在不同微控制器上的移植提供基礎(chǔ),也可為不同實時操作系統(tǒng)下避免優(yōu)先級反轉(zhuǎn)問題機制的比較分析提供參考。
“火星探路者”于1997年7月4日在火星表面著陸。在開始的幾天內(nèi)工作穩(wěn)定,并傳回大量數(shù)據(jù),但是幾天后,“探路者”開始出現(xiàn)系統(tǒng)復(fù)位、數(shù)據(jù)丟失的現(xiàn)象。究其原因是發(fā)生了優(yōu)先級反轉(zhuǎn)問題[4]。
當(dāng)線程以獨占方式使用共享資源時,可能出現(xiàn)低優(yōu)先級線程先于高優(yōu)先級線程被運行的現(xiàn)象,這就是優(yōu)先級反轉(zhuǎn)問題,可進(jìn)行如下一般性描述。
假設(shè)有三個線程Ta、Tb、Tc,其優(yōu)先級分別記為Pa、Pb、Pc,且有Pa>Pb>Pc,Ta和Tc使用一個共享資源S,Tb并不使用共享資源S,用信號量x(x=0,1)標(biāo)識對S的獨占訪問,初始時x=1,表示可獨占S。設(shè)t0時刻,Tc先運行并獲取信號量(即x由1變?yōu)?,表示S已被占用),使用S。t1時刻,Ta被調(diào)度運行,因為Pa>Pc,故搶占Tc獲得CPU使用權(quán)。Ta運行至t2時刻,需訪問S,但Tc并沒有釋放S(即x仍為0),所以Ta放入阻塞隊列,直到x=1,才能從阻塞隊列中移出,放入就緒隊列,被重新調(diào)度運行。t3時刻,Tb搶占Tc獲得運行,此時就出現(xiàn)了Tb雖然優(yōu)先級比Ta低,卻比Ta先運行的現(xiàn)象,不合理,這就是優(yōu)先級反轉(zhuǎn)問題。表1給出了上述過程的運行時序。
表1 優(yōu)先級反轉(zhuǎn)過程
對于嵌入式實時操作系統(tǒng)來說,最重要的指標(biāo)在于確認(rèn)線程執(zhí)行的時間是可預(yù)測的,即要確保在任何時刻執(zhí)行某個線程都不能超過某個特定的時間。然而由于本身基于優(yōu)先級設(shè)計的線程,每個優(yōu)先級不同的線程往往對應(yīng)著現(xiàn)實中執(zhí)行的任務(wù),若發(fā)生了優(yōu)先級反轉(zhuǎn),會導(dǎo)致低優(yōu)先級線程比高優(yōu)先級線程先執(zhí)行,造成線程調(diào)度時時間的不確定性,破壞實時系統(tǒng)的實時性,嚴(yán)重時可能導(dǎo)致系統(tǒng)崩潰。因此處理好這類問題對于實時操作系統(tǒng)的正常運行至關(guān)重要。
常見的避免優(yōu)先級反轉(zhuǎn)方法一般有兩種,分別為優(yōu)先級繼承和優(yōu)先級天花板。
(1) 優(yōu)先級繼承。優(yōu)先級繼承(priority inheritance)是指通過臨時提升持有資源的低優(yōu)先級線程的優(yōu)先級至請求訪問同一資源的高優(yōu)先級線程的優(yōu)先級來避免優(yōu)先級反轉(zhuǎn)的方法[5]。
(2)優(yōu)先級天花板。優(yōu)先級天花板(priority ceiling)是指通過將申請并得到資源的線程的優(yōu)先級臨時提升至所有可能使用該資源的線程中最高優(yōu)先級線程的優(yōu)先級(即天花板)來避免優(yōu)先級反轉(zhuǎn)的方法[5]。
許多嵌入式實時操作系統(tǒng),例如μC/OS-Ⅱ、VxWorks、WinCE等針對這一問題都有自己的處理方式。μC/OS-Ⅱ操作系統(tǒng)提供互斥信號量mutex來避免優(yōu)先級反轉(zhuǎn),其利用的是優(yōu)先級置頂協(xié)議[6]。VxWorks操作系統(tǒng)對優(yōu)先級反轉(zhuǎn)問題采用優(yōu)先級繼承協(xié)議,整體上使用互斥信號量,按優(yōu)先級與先入先出隊列兩種方式排列等待對該互斥信號量進(jìn)行上鎖的線程,在創(chuàng)建互斥信號量時,選擇SEM_INVERSION_ SAFE與SEM_Q_PRIORITY兩種選擇值的域,即可避免優(yōu)先權(quán)反轉(zhuǎn)[7]。WinCE實時操作系統(tǒng)軟件采用優(yōu)先級繼承方式來避免優(yōu)先級反轉(zhuǎn),當(dāng)高優(yōu)先級線程被低優(yōu)先級線程阻塞時,會提升低優(yōu)先級線程的優(yōu)先級至高優(yōu)先級線程的優(yōu)先級,從而避免優(yōu)先級反轉(zhuǎn)現(xiàn)象[8]。
本文將使用一個測試工程來對mbedOS避免優(yōu)先級反轉(zhuǎn)問題機制進(jìn)行流程分析。首先對部分關(guān)鍵代碼進(jìn)行分析,然后給出調(diào)度時序,最后對執(zhí)行流程進(jìn)行分段解析。測試工程的功能是:創(chuàng)建三個用戶線程Ta、Tb、Tc,初始優(yōu)先級分別設(shè)置為26、25、24(在mbedOS中數(shù)字越大優(yōu)先級越高,將此處三個線程的優(yōu)先級分別命名為Pa、Pb、Pc),啟動運行順序為Tc、Ta、Tb,其中Tc和Ta使用同一互斥量來申請對共享資源S的獨占使用,Tb并不使用共享資源S。測試工程通過串口輸出三個線程調(diào)度時使用互斥量避免優(yōu)先級反轉(zhuǎn)的詳細(xì)過程。為了確保整體執(zhí)行流程能夠循環(huán)執(zhí)行,過程中使用到了延時函數(shù)(會將線程放入延時隊列),此處模擬線程到達(dá)的先后順序為:經(jīng)過1秒后Tc到達(dá),再經(jīng)過4秒后Ta、Tb到達(dá)(注意此處到達(dá)順序為先Ta后Tb)。測試工程的執(zhí)行流程如圖1所示。
圖1 測試工程執(zhí)行流程
3.1.1互斥量控制塊
在mbedOS中使用互斥量控制塊的方式來描述互斥量,數(shù)據(jù)結(jié)構(gòu)如下:
typedef struct osRtxMutex_s
{
uint8_t id;
//互斥量ID
uint8_t state;
//互斥量狀態(tài)
uint8_t flags;
//互斥量標(biāo)志
uint8_t attr;
//互斥量屬性
const char *name;
//互斥量名稱
osRtxThread_t *thread_list;
//互斥量阻塞隊列
osRtxThread_t *owner_thread;
//互斥量私有線程
struct osRtxMutex_s *owner_prev;
//指向前一個互斥量
struct osRtxMutex_s *owner_next;
//指向下一個互斥量
uint8_t lock;
//互斥鎖
uint8_t padding[3];
//保留
} osRtxMutex_t;
其中互斥量屬性attr包括嵌套型互斥量(osMutexRecursive)、內(nèi)部優(yōu)先級互斥量(osMutexPrioInherit)和健壯互斥量(osMutexRobust)。當(dāng)高優(yōu)先級的線程等待已被低優(yōu)先級的線程鎖定互斥量時,若該互斥量擁有內(nèi)部優(yōu)先級屬性,則低優(yōu)先級的線程會以高優(yōu)先級線程的優(yōu)先級運行,這種方式是以繼承的形式進(jìn)行傳遞的。當(dāng)線程解鎖互斥量時,線程的優(yōu)先級自動變?yōu)樗瓉淼膬?yōu)先級。mbedOS正是利用了這一關(guān)鍵屬性來避免優(yōu)先級反轉(zhuǎn)問題。
3.1.2互斥量鎖定函數(shù)
在mbedOS中,線程可通過使用互斥量對象調(diào)用lock函數(shù)的方式申請鎖定互斥量。lock函數(shù)內(nèi)部調(diào)用順序為lock→osMutexAcquire→_ _svcMutexAcquire→觸發(fā)SVC中斷服務(wù)程序SVC_Handler→實際互斥量鎖定函數(shù)svcRtxMutexAcquire。
其中涉及到優(yōu)先級部分的關(guān)鍵代碼如下:
if ((mutex->attr & osMutexPrioInherit) !=0U) {
if (mutex->owner_thread->priority
mutex->owner_thread->priority=thread->priority;
osRtxThreadListSort(mutex->owner_thread);
}
}
代碼段分析:if((mutex->attr & osMutexPrioInherit)!=0U)語句表示判斷該互斥量對象是否具有內(nèi)部優(yōu)先級屬性,若包含則進(jìn)一步判斷該互斥量私有線程的優(yōu)先級mutex->owner_thread->priority是否小于當(dāng)前運行線程的優(yōu)先級thread->priority,若小于則將當(dāng)前運行線程的優(yōu)先級賦值給該互斥量私有線程的優(yōu)先級,即所謂的優(yōu)先級繼承。
3.1.3互斥量解鎖函數(shù)
在mbedOS中,線程可通過使用互斥量對象調(diào)用unlock函數(shù)的方式申請解鎖互斥量。unlock函數(shù)內(nèi)部調(diào)用順序為unlock→osMutexRelease→_ _svcMutexRelease→觸發(fā)SVC中斷服務(wù)程序SVC_Handler→實際互斥量解鎖函數(shù)svcRtxMutexRelease。
其中涉及到優(yōu)先級部分的關(guān)鍵代碼如下:
if ((mutex->attr & osMutexPrioInherit)!=0U) {
priority=thread->priority_base;
mutex0=thread->mutex_list;
while (mutex0 != NULL) {
if ((mutex0->thread_list!=NULL) && (mutex0->thread_list->priority>priority)) {
priority=mutex0->thread_list->priority;
}
mutex0=mutex0->owner_next;
}
thread->priority=priority;
}
代碼段分析:if((mutex->attr & osMutexPrioInherit)!=0U)語句表示判斷該互斥量是否具有內(nèi)部優(yōu)先級屬性,priority和mutex0均表示臨時局部變量,thread表示當(dāng)前運行線程。若該互斥量包含內(nèi)部優(yōu)先級屬性,則首先獲取當(dāng)前運行線程初始化時的優(yōu)先級thread->priority_base賦值給priority以及當(dāng)前運行線程所擁有的互斥量列表指針thread->mutex_list賦值給mutex0。然后使用while循環(huán)找到mutex0所在的互斥量列表中的所有互斥量所擁有的所有線程,獲取優(yōu)先級最高的線程的優(yōu)先級并賦值給priority。最后將當(dāng)前運行線程的優(yōu)先級變?yōu)閜riority。
特別要指出的是,上述while循環(huán)語句是為了避免當(dāng)?shù)蛢?yōu)先級線程與多個高優(yōu)先級線程嵌套使用多個互斥量時可能造成的優(yōu)先級反轉(zhuǎn)現(xiàn)象。由于只有當(dāng)系統(tǒng)中存在三個或三個以上線程時才可能發(fā)生優(yōu)先級反轉(zhuǎn)現(xiàn)象,故此處以四個線程為例。假設(shè)有兩個高優(yōu)先級線程Ta和Tb、一個中優(yōu)先級線程Tc以及一個低優(yōu)先級線程Td,優(yōu)先級分別為Pa、Pb、Pc、Pd,其中Pa>Pb>Pc>Pd,Ta和Td使用同一互斥量mutex1共享資源S1, Tb和Td使用同一互斥量mutex2共享資源S2,且Td首先申請鎖定互斥量mutex2,再在釋放mutex2之前申請鎖定互斥量mutex1(連續(xù)鎖定多個互斥量遵循后進(jìn)先出原則,即后鎖定的互斥量先釋放)。設(shè)t0時刻,Td首先到達(dá)并鎖定了互斥量mutex2和互斥量mutex1。t1時刻Ta和Tb到達(dá),等待Td解鎖各自需要的互斥量。Tc也在t1時刻到達(dá),由于Ta和Tb調(diào)用互斥量鎖定函數(shù)svcRtxMutexAcquire時已將Td的優(yōu)先級提升至最高值Pa,故Tc會進(jìn)入等待狀態(tài),等待Td運行。t2時刻,Td解鎖互斥量mutex1,若此時未使用上述while循環(huán)語句,Td會直接降為其初始優(yōu)先級Pd,此時Ta會搶占Td獲得CPU使用權(quán),鎖定互斥量mutex1并執(zhí)行。t3時刻,Ta運行完畢,此時處于等待狀態(tài)的Td由于優(yōu)先級低于Tc,會被Tc搶占CPU使用權(quán),使得Td無法解鎖互斥量mutex2,從而導(dǎo)致優(yōu)先級低于Tb的線程Tc卻先于Tb運行,造成優(yōu)先級反轉(zhuǎn)現(xiàn)象,具體過程如表2所示。
表2 四線程優(yōu)先級反轉(zhuǎn)過程
續(xù)表2
若只涉及到一個低優(yōu)先級線程和一個高優(yōu)先級線程對于同一個互斥量的使用,則在低優(yōu)先級線程(即上述代碼中的thread)解鎖互斥量的過程中,由于thread擁有的互斥量列表已在執(zhí)行上述代碼之前釋放,故mutex0為空,while循環(huán)實際上并不執(zhí)行,而是直接通過語句:
priority=thread->priority_base;
thread->priority=priority;
將thread的優(yōu)先級轉(zhuǎn)變?yōu)槠涑跏純?yōu)先級。
本文的測試工程在STM32微控制器[13]上進(jìn)行。STM32片內(nèi)Flash存儲區(qū)大小為256 KB,一般用來存放中斷向量、程序代碼、常量等;片內(nèi)RAM存儲區(qū)大小為32 KB,一般用于存放初始化的全局變量、靜態(tài)變量、局部變量等。
3.2.1線程調(diào)度時序分析
測試工程涉及到的三個線程具體調(diào)度時序圖如圖2所示。
圖2 線程調(diào)度時序圖
其中“?”表示線程或隊列的有效運行時間,實線箭頭表示線程運行、進(jìn)入隊列、申請互斥量或改變優(yōu)先級,虛線箭頭表示從隊列取線程(互斥量)或返回申請互斥量結(jié)果。
3.2.2執(zhí)行流程分段解析
目前,國內(nèi)外針對嵌入式實時操作系統(tǒng)的優(yōu)先級反轉(zhuǎn)問題采用的分析方法,大多為一個基于示例圖的淺層實驗[9-10],均沒有完整地將處理機制分析清楚。針對上述問題,本文將采用基于時序圖并在代碼中相應(yīng)位置插入printf語句的方法進(jìn)行分析。插入printf語句的分析方式是應(yīng)用最廣泛的調(diào)試技術(shù)之一,可應(yīng)用于計算機視覺中來輸出中間結(jié)果[11],亦可應(yīng)用于程序功能分析中得到故障產(chǎn)生的信息[12]。該方法具有簡單、清晰、直觀等優(yōu)點。
(1) 線程啟動。從芯片上電到mbedOS 啟動完成后,會最終轉(zhuǎn)到app_init函數(shù)執(zhí)行,然后可在app_init函數(shù)中創(chuàng)建用戶線程[3]。在該函數(shù)中創(chuàng)建并先后啟動了三個用戶線程Tc、Ta、Tb,然后阻塞該函數(shù)的運行。為確保線程能正常被創(chuàng)建,不被其他線程打斷,在創(chuàng)建用戶線程的過程中,使用了互斥量。
printf輸出結(jié)果如下:
0-1.當(dāng)前運行的主線程(2000FF80)啟動線程Tc.
4-1.互斥量(20001694)的互斥鎖=0,表示未鎖定,當(dāng)前運行線程(2000FF80)可以申請該互斥量
4-2.互斥鎖變?yōu)?,表示互斥量申請成功
8.互斥鎖變?yōu)?,表示完全解鎖,將當(dāng)前互斥量(20001694)從當(dāng)前運行線程(2000FF80)擁有的互斥量列表(2000FFAC)中移除
*9-1.當(dāng)前線程=2000FF80的初始優(yōu)先級=24,當(dāng)前優(yōu)先級=24
*9-2.釋放互斥量后,當(dāng)前線程=2000FF80的初始優(yōu)先級=24,當(dāng)前優(yōu)先級=24
0-2.當(dāng)前運行的主線程(2000FF80)啟動線程Ta.
4-1.互斥量(20001814)的互斥鎖=0,表示未鎖定,當(dāng)前運行線程(2000FF80)可以申請該互斥量
4-2.互斥鎖變?yōu)?,表示互斥量申請成功
8.互斥鎖變?yōu)?,表示完全解鎖,將當(dāng)前互斥量(20001814)從當(dāng)前運行線程(2000FF80)擁有的互斥量列表(2000FFAC)中移除
*9-1.當(dāng)前線程=2000FF80的初始優(yōu)先級=24,當(dāng)前優(yōu)先級=24
*9-2.釋放互斥量后,當(dāng)前線程=2000FF80的初始優(yōu)先級=24,當(dāng)前優(yōu)先級=24
0-3.當(dāng)前運行的主線程(2000FF80)啟動線程Tb.
4-1.互斥量(20001754)的互斥鎖=0,表示未鎖定,當(dāng)前運行線程(2000FF80)可以申請該互斥量
4-2.互斥鎖變?yōu)?,表示互斥量申請成功
8.互斥鎖變?yōu)?,表示完全解鎖,將當(dāng)前互斥量(20001754)從當(dāng)前運行線程(2000FF80)擁有的互斥量列表(2000FFAC)中移除
*9-1.當(dāng)前線程=2000FF80的初始優(yōu)先級=24,當(dāng)前優(yōu)先級=24
*9-2.釋放互斥量后,當(dāng)前線程=2000FF80的初始優(yōu)先級=24,當(dāng)前優(yōu)先級=24
******Tc、Ta和Tb啟動完成,同時阻塞主線程******
(2) Tc申請鎖定互斥量。阻塞主線程后,由于初始時Tc延時1秒,而Ta和Tb延時5秒,故Tc會先從延時隊列中移出放入就緒隊列,搶占空閑線程獲得CPU使用權(quán)。由于互斥鎖為0,Tc申請鎖定互斥量成功,互斥鎖變?yōu)?,同時點亮藍(lán)燈。
printf輸出結(jié)果如下:
5.當(dāng)前就緒隊列中最高優(yōu)先級線程(20001834)取代當(dāng)前運行線程(200016B4),開始運行
1.Tc(200016B4)獲得CPU使用權(quán),藍(lán)燈亮
1-1.Tc申請鎖定互斥量
4-1.互斥量(20001880)的互斥鎖=0,表示未鎖定,當(dāng)前運行線程(200016B4)可以申請該互斥量
4-2.互斥鎖變?yōu)?,表示互斥量申請成功
1-2.Tc鎖定互斥量成功,將鎖定15秒
(3) Ta申請鎖定互斥量。在Tc鎖定互斥量4秒后,Ta和Tb從延時隊列中移出,放入到就緒隊列,由于Pa大于Pb,故mbedOS會從就緒隊列中取出Ta激活運行。又因為Pa大于Pc,故Ta會搶占Tc獲得CPU使用權(quán),Tc被放入就緒隊列,同時熄滅藍(lán)燈。
但當(dāng)Ta運行至申請鎖定互斥量時,由于此時互斥量已被Tc鎖定(互斥鎖為1),Ta申請互斥量失敗,因此會將Tc的優(yōu)先級提升至與Ta的優(yōu)先級相同(即使用優(yōu)先級繼承方法將Tc的優(yōu)先級提升至Pa),然后將Tc放入就緒隊列重新排序,Ta自身進(jìn)入等待隊列和互斥量阻塞隊列,將CPU使用權(quán)讓給Tc,等待Tc解鎖互斥量。
printf輸出結(jié)果如下:
5.當(dāng)前就緒隊列中最高優(yōu)先級線程(20001834)取代當(dāng)前運行線程(200016B4),開始運行
2.Ta(20001834)搶占Tc獲得CPU使用權(quán),藍(lán)燈暗
2-1.Ta申請鎖定互斥量
6.互斥鎖=1,表示已鎖定(其所有者線程=200016B4),互斥量申請失敗
*7-1.優(yōu)先級繼承前,當(dāng)前互斥量私有線程=200016B4的優(yōu)先級=24低于當(dāng)前運行線程=20001834的優(yōu)先級=26
*7-2.優(yōu)先級繼承后,當(dāng)前互斥量私有線程=200016B4的優(yōu)先級被提升至與當(dāng)前運行線程=20001834的優(yōu)先級=26相同
*7-3.將當(dāng)前互斥量私有線程=200016B4從互斥量私有線程列表中移出,并放到就緒隊列(200001D0)中重新排序
6-1.將當(dāng)前運行線程(20001834)放到等待隊列(200001E4)
6-2.從就緒隊列(200001D0)獲取優(yōu)先級最高的線程(200016B4),并設(shè)置為激活態(tài)準(zhǔn)備運行
6-3.將當(dāng)前運行線程(20001834)放入互斥量阻塞隊列(20001888):20001834->0->80019A9
(4) Tc解鎖互斥量。Tc重新獲得CPU使用權(quán)后,繼續(xù)運行。由于互斥量是由Tc鎖定的,因此Tc能成功解鎖互斥量。在解鎖過程中,由于當(dāng)前運行線程Tc將互斥量從它的互斥量列表中移出,故此時mutex0為空,因此在互斥量解鎖函數(shù)svcRtxReleaseMutex中會直接將Tc的優(yōu)先級降為其初始優(yōu)先級Pc。解鎖后互斥鎖為0,同時點亮藍(lán)燈,Tc放入就緒隊列,又開始等待執(zhí)行新一輪的執(zhí)行過程。此時互斥量會從互斥量列表移出,并移轉(zhuǎn)給正在等待互斥量的Ta,之后Ta放入就緒隊列,由于Pa>Pb>Pc,故在就緒隊列中Ta處于隊首位置。mbedOS從就緒隊列中取出優(yōu)先級最高的Ta激活運行,Ta成功鎖定互斥量,互斥鎖變?yōu)?。
printf輸出結(jié)果如下:
1-3.Tc解鎖互斥量成功,藍(lán)燈亮
8.互斥鎖變?yōu)?,表示完全解鎖,將當(dāng)前互斥量(20001880)從當(dāng)前運行線程(200016B4)擁有的互斥量列表(200016E0)中移除
*9-1.當(dāng)前線程=200016B4的初始優(yōu)先級=24,當(dāng)前優(yōu)先級=26
*9-2.釋放互斥量后,當(dāng)前線程=200016B4的初始優(yōu)先級=24,當(dāng)前優(yōu)先級=24
10-1.從互斥量阻塞隊列(20001888)中獲取優(yōu)先級最高的互斥量等待線程=20001834
10-2.將線程(20001834)從等待隊列(200001E4)中移出
10-3.將線程(20001834)放到就緒隊列(200001D0)
11.此時就緒隊列=200001D0中的線程:20001834->20001774->20001328
10-4.將剛獲取的線程(20001834)設(shè)置為互斥量所有者,互斥鎖變?yōu)?
(5) Ta解鎖互斥量。Ta運行5秒后,由于互斥量是由Ta鎖定的,因此Ta能成功解鎖互斥量,解鎖后互斥鎖為0,同時熄滅藍(lán)燈。互斥量從互斥量列表移出,同時為了重復(fù)上述演示過程,Ta放入延時隊列5秒,5秒之后從延時隊列移出放入就緒隊列,又開始等待執(zhí)行新一輪的執(zhí)行過程。
printf輸出結(jié)果如下:
5.當(dāng)前就緒隊列中最高優(yōu)先級線程(20001834)取代當(dāng)前運行線程(200016B4),開始運行
2-2.Ta鎖定互斥量成功,將鎖定5秒
2-3.Ta解鎖互斥量成功,藍(lán)燈暗
8.互斥鎖變?yōu)?,表示完全解鎖,將當(dāng)前互斥量(20001880)從當(dāng)前運行線程(20001834)擁有的互斥量列表(20001860)中移除
*9-1.當(dāng)前線程=20001834的初始優(yōu)先級=26,當(dāng)前優(yōu)先級=26
*9-2.釋放互斥量后,當(dāng)前線程=20001834的初始優(yōu)先級=26,當(dāng)前優(yōu)先級=26
(6) Tb運行。在Ta進(jìn)入延時隊列后,mbedOS從就緒隊列中取出優(yōu)先級最高的Tb激活運行。Tb運行5秒后釋放CPU使用權(quán),為了重復(fù)上述演示過程,Tb放入延時隊列4秒,之后從延時隊中移出放入就緒隊列,又開始等待執(zhí)行新一輪的執(zhí)行過程。printf輸出結(jié)果如下:
3.Tb(20001774)獲得CPU使用權(quán),將運行5秒,成功避免優(yōu)先級反轉(zhuǎn)
5.當(dāng)前就緒隊列中最高優(yōu)先級線程(20001834)取代當(dāng)前運行線程(20001774),開始運行
3-1.taskB釋放CPU使用權(quán)
其中地址2000FF80表示主線程,地址200016B4表示Tc,地址20001834表示Ta,地址20001774表示Tb,地址80019A9表示缺省處理函數(shù)DefaultISR,地址20001328表示空閑線程(系統(tǒng)中無運行線程時該線程被調(diào)度運行)。
通過上述流程分析,mbedOS中避免優(yōu)先級反轉(zhuǎn)問題機制可歸納如下:
mbedOS整體上使用的是基于互斥量的優(yōu)先級繼承方法來避免優(yōu)先級反轉(zhuǎn),其中優(yōu)先級繼承方法包括優(yōu)先級提升與優(yōu)先級恢復(fù)兩部分。當(dāng)線程調(diào)用svcRtxMutexAcquire函數(shù)申請鎖定互斥量時,系統(tǒng)首先判斷該互斥量是否已鎖定,若已鎖定,則進(jìn)一步判斷該互斥量是否具有內(nèi)部優(yōu)先級屬性osMutexPrioInherit,若是,則通過語句mutex->owner_thread->priority=thread->priority將當(dāng)前運行線程(thread)的優(yōu)先級賦給該互斥量的私有線程(mutex->owner_thread),即優(yōu)先級繼承方法中的優(yōu)先級提升;當(dāng)線程調(diào)用svcRtxMutexRelease函數(shù)申請鎖定互斥量時,系統(tǒng)首先判斷該互斥量是否具有內(nèi)部優(yōu)先級屬性osMutexPrioInherit,若是,則獲取當(dāng)前運行線程的互斥量列表,若除了該互斥量以外,還具有其他互斥量,則遍歷其他互斥量的私有線程列表,獲取其中最高優(yōu)先級線程的優(yōu)先級,并將其賦給當(dāng)前運行線程,否則直接將當(dāng)前運行線程優(yōu)先級變?yōu)槠涑跏純?yōu)先級,即優(yōu)先級繼承方法中的優(yōu)先級恢復(fù)。
優(yōu)先級反轉(zhuǎn)問題是每一個實時操作系統(tǒng)所必須要考慮到的問題,一旦出現(xiàn),就會破壞系統(tǒng)的實時性,輕則造成系統(tǒng)邏輯紊亂,嚴(yán)重時甚至?xí)?dǎo)致系統(tǒng)崩潰。本文首先通過歷史上出現(xiàn)過的問題引出優(yōu)先級反轉(zhuǎn),并分析其現(xiàn)象、避免的意義以及基本解決方案,然后利用Cortex-M4內(nèi)核的STM32微控制器,基于Kinetis Design Studio 3.1.0 IDE開發(fā)環(huán)境和SD-mbedOS工程框架,構(gòu)建一個測試工程對mbedOS避免優(yōu)先級反轉(zhuǎn)問題的機制進(jìn)行了詳細(xì)剖析。通過剖析,有助于快速理解mbedOS中互斥量的使用以及避免優(yōu)先級反轉(zhuǎn)問題的詳細(xì)過程,可為 mbedOS的應(yīng)用研究和在不同微控制器上的移植提供基礎(chǔ),也可為不同實時操作系統(tǒng)下避免優(yōu)先級反轉(zhuǎn)問題機制的比較分析提供參考。