樓亮亮,周苗,鮑星合
(1.中國科學(xué)院 上海微系統(tǒng)與信息技術(shù)研究所 無線傳感網(wǎng)與通信重點實驗室,上海201800;2.上海大學(xué))
Protothread是完全基于事件驅(qū)動的操作系統(tǒng),只在語言層面上做了相應(yīng)的“封裝”。因此,Protothreads具有一定的局限性[2]。Protothread不支持優(yōu)先級搶占,阻塞的I/O只允許在主線程函數(shù)中調(diào)用等。針對上述的幾個問題,本文提出的操作系統(tǒng)是在Protothread基礎(chǔ)上的改進(jìn),具有低功耗、易移植、支持多任務(wù)切換、時間片調(diào)度、搶占式調(diào)度及事件同步等特點。由于本系統(tǒng)使用了Protothread的核心思想,這也決定了本系統(tǒng)具有一定的局限,在每個任務(wù)中避免與switch語句合用[5]。
本系統(tǒng)主要由調(diào)度內(nèi)核、中斷管理系統(tǒng)及軟中斷系統(tǒng)組成,具體框架如圖1所示。
在本系統(tǒng)中,每個任務(wù)都定義與該任務(wù)相關(guān)的任務(wù)控制塊(TCB)。將所有任務(wù)控制塊組裝成一個隊列,則每個任務(wù)控制塊都對應(yīng)于任務(wù)控制塊隊列中的一個節(jié)點,每個任務(wù)控制塊中的數(shù)據(jù)只能由中斷程序或者調(diào)度內(nèi)核來修改。任務(wù)控制塊中包含該任務(wù)的入口地址、支持時間片調(diào)度的服務(wù)延時、信號量及當(dāng)前任務(wù)的狀態(tài)。為了節(jié)省內(nèi)存,其中信號量和當(dāng)前任務(wù)狀態(tài)采用位定義。其數(shù)據(jù)結(jié)構(gòu)如下所示 :
圖1 系統(tǒng)的架構(gòu)
本系統(tǒng)支持優(yōu)先級調(diào)度。任務(wù)的優(yōu)先級對應(yīng)于其任務(wù)的控制塊在任務(wù)控制塊隊列中的位置,任務(wù)控制塊在該隊列中的位置越靠前,該任務(wù)就越早被調(diào)用,優(yōu)先級也就越高。
本系統(tǒng)的任務(wù)狀態(tài)劃分為3種:空閑、就緒及運行。在任何時刻,任務(wù)的狀態(tài)必定是這3種狀態(tài)中的一個。本文涉及的系統(tǒng)與一般嵌入式操作系統(tǒng)(如μC/OS、FreeRTOS等)設(shè)計方式不同,本系統(tǒng)中的任何任務(wù)都不是無限循環(huán),且都有返回值,任務(wù)在阻塞的時候會返回相應(yīng)的狀態(tài)給調(diào)度內(nèi)核,任務(wù)的無限循環(huán)只能由調(diào)度內(nèi)核來實現(xiàn)。任務(wù)的返回值為一個8位有符號數(shù),該值的正數(shù)部分0~127留給任務(wù)申請延時服務(wù)的節(jié)拍數(shù),而-128~-1則作為信號量、任務(wù)執(zhí)行結(jié)束等其他相應(yīng)操作的返回值。任務(wù)把該值返回到內(nèi)核后,內(nèi)核根據(jù)該值的數(shù)據(jù)作出相應(yīng)操作。
OS_TASK_END_RET是用來指示當(dāng)前運行的任務(wù)執(zhí)行完所有操作的返回值,內(nèi)核得到該返回值后會重新運行該任務(wù),實現(xiàn)任務(wù)的無限循環(huán);OS_TASK_SEM_RET用來指示當(dāng)前運行的任務(wù)有信號量發(fā)出,內(nèi)核得到該返回值后,根據(jù)該值把該任務(wù)TCB中的變量sem設(shè)置成指定的數(shù)據(jù)值,且把該任務(wù)置為就緒態(tài)(如果有其他任務(wù)向該任務(wù)發(fā)出信號量,則執(zhí)行該任務(wù));OS_TASK_TIME_RET用來指示當(dāng)前運行的任務(wù)申請延時運行服務(wù)的返回節(jié)拍數(shù)(該值為正數(shù)),內(nèi)核得到該返回值后,將其賦予該任務(wù)TCB中的變量timeout。如果每個任務(wù)TCB中的變量timeout≥0,則該值在Tick中斷中實現(xiàn)減1操作,一旦該值減至為0,內(nèi)核就會重新運行該TCB對應(yīng)的任務(wù)。
在系統(tǒng)開始調(diào)度的時候,調(diào)度內(nèi)核首先檢查pid是否超出了系統(tǒng)所定義的任務(wù)總數(shù)。如果超過任務(wù)總數(shù),說明系統(tǒng)已經(jīng)執(zhí)行完所有用戶指定的任務(wù),則調(diào)度內(nèi)核停止調(diào)度且執(zhí)行空閑任務(wù),進(jìn)入低功耗模式??臻e任務(wù)的退出,只有pid數(shù)值在事件中斷或Tick中斷中被改變才會實現(xiàn),一旦退出空閑任務(wù),系統(tǒng)則重新開始調(diào)度。
如果pid小于系統(tǒng)所定義的任務(wù)總數(shù),則系統(tǒng)開始從TCB隊列中獲取每個TCB中變量status的數(shù)值,該變量用于記錄任務(wù)狀態(tài)。如果任務(wù)狀態(tài)是就緒態(tài),則加載任務(wù)TCB中的入口函數(shù)地址到內(nèi)核中執(zhí)行。在任務(wù)執(zhí)行的過程中,一旦任務(wù)被阻塞或執(zhí)行結(jié)束,任務(wù)就會返回相應(yīng)的數(shù)值到調(diào)度內(nèi)核中,調(diào)度內(nèi)根據(jù)該返回值,開展相應(yīng)工作。
具體調(diào)度算法流程如圖2所示。
圖2 調(diào)度算法流程圖
為了支持一級任務(wù)優(yōu)先的搶占式調(diào)度功能,本系統(tǒng)采用了軟中斷模式,利用中斷來自動壓棧與出棧,把高優(yōu)先級的任務(wù)安排在軟中斷服務(wù)程序中運行。由于軟件中斷優(yōu)先級比較低,系統(tǒng)運行基本不受影響。例如在Tick中斷中,有高優(yōu)先級任務(wù)就緒,如果直接在Tick中斷中運行,由于任務(wù)運行帶來時間的不確定性,會嚴(yán)重影響Tick的定時精度。而采用軟中斷的模式,由于中斷優(yōu)先級比較低,在其服務(wù)程序中運行高優(yōu)先級的任務(wù)時,其他中斷(如Tick中斷)不會受到影響。
這將帶來兩個方面的好處:①如果高優(yōu)先級任務(wù)在中斷服務(wù)中直接執(zhí)行,將會影響Tick定時器的定時精度或者是其他中斷的響應(yīng)時間。②利用軟中斷自動壓棧出棧功能,可以減少上下文切換,降低RAM的需要,從而提高了效率,降低了系統(tǒng)的整體功耗。
本系統(tǒng)和其他內(nèi)核一樣,需要時鐘節(jié)拍來實現(xiàn)時間片調(diào)度和延時服務(wù)。在本系統(tǒng)中實現(xiàn)上述服務(wù)的函數(shù)為OS_TIME_DLY()。調(diào)用該函數(shù)之后,該任務(wù)會返回延時的節(jié)拍數(shù)到調(diào)度內(nèi)核中,內(nèi)核會把返回的節(jié)拍數(shù)寫入該任務(wù)TCB中的變量timeout,內(nèi)核實現(xiàn)一次調(diào)度,執(zhí)行下一個就緒態(tài)任務(wù)。具體實現(xiàn)如下所示:
#define OS_TIME_DLY(ticks)do{_lc=__LINE__;return ticks;}while(0);case__LINE__:
為了實現(xiàn)時間片調(diào)度,需要在Tick中斷中調(diào)用OS_TIME_UPDATE(),來更新每個任務(wù)TCB中變量timeout的數(shù)據(jù)值。如果該變量數(shù)據(jù)≥0,則在每次中斷中進(jìn)行減1操作,否則不做任何處理。當(dāng)任務(wù)TCB中變量timeout減至0,則把對應(yīng)的任務(wù)置為就緒態(tài),內(nèi)核在調(diào)度的時候就會執(zhí)行就緒態(tài)的任務(wù),從而實現(xiàn)時間片調(diào)度和延時服務(wù)功能。該函數(shù)的具體流程如圖3(a)所示。
本系統(tǒng)中事件同步采用了信號量設(shè)計方式,涉及到該項服務(wù)的有兩個函數(shù):等待信號量與發(fā)送信號量。若一個任務(wù)等待一個信號量,則調(diào)用OS_SEM_PEND()阻塞該任務(wù)并返回OS_TASK_SEM_RET到調(diào)度內(nèi)核,調(diào)度內(nèi)核根據(jù)該返回值把相應(yīng)狀態(tài)寫入該任務(wù)TCB中的變量sem,并把該任務(wù)置為就緒狀態(tài)。具體實現(xiàn)如下所示:
#define OS_SEM_PEND()do{_lc=__LINE__;return OS_TASK_SEM_RET ;}while(0);case__LINE__:
發(fā)送一個信號量函數(shù),實現(xiàn)相對等待信號量比較復(fù)雜,需要涉及到任務(wù)的切換。當(dāng)某個任務(wù)或者中斷中調(diào)用OS_SEM_POST()時,該函數(shù)在執(zhí)行結(jié)束后返回要發(fā)送信號量的任務(wù)ID號到內(nèi)核中,調(diào)度內(nèi)核會判斷該任務(wù)TCB中R變量sem是否在等待該信號量。如果是,則執(zhí)行任務(wù)切換,即開啟軟中斷。涉及到的函數(shù)如下:
INT8S OS_SEM_POST(INT8S (*ptask)(void))
其中:ptask為要發(fā)送信號量到的任務(wù)名?;玖鞒倘鐖D3(b)所示。
本文涉及的系統(tǒng)是在IAR FOR MSP430 V5.30.1執(zhí)行,采用的編譯模式為:Release、優(yōu)化等級為Level high balanced。表1略——編者注。
綜上所述,本系統(tǒng)所帶來的RAM額外消耗可由以下式計算得出:
RAMoverhead=任務(wù)數(shù)×5字節(jié)+1字節(jié)
圖3 時間片調(diào)度算法流程與信號量發(fā)送流程
本系統(tǒng)在MSP430F149平臺上驗證其正確性及可靠性。在實驗中采用定時器A作為系統(tǒng)的Tick時鐘,為了測量每個任務(wù)的運行時間及切換時間,本系統(tǒng)中Tick定時周期為16μs。在實際應(yīng)用中,用戶可以根據(jù)系統(tǒng)的實際應(yīng)用場合而定,系統(tǒng)Tick周期越短則系統(tǒng)負(fù)荷越大。由于MSP430系統(tǒng)微處理器沒有相關(guān)軟中斷指令,故采用定時器B作為軟中斷來實現(xiàn)任務(wù)的切換,其優(yōu)先級相對定時器A與其他中斷來說比較低。
為了驗證其正確性,本文在該系統(tǒng)平臺上建立了兩個任務(wù)。同時系統(tǒng)會自動增加一個空閑任務(wù),兩個任務(wù)都不需要執(zhí)行的時候,系統(tǒng)就會自動進(jìn)入空閑模式(即休眠模式),降低了功耗。
任務(wù)1優(yōu)先級最高,創(chuàng)建的時候設(shè)置其為就緒狀態(tài),所以在任務(wù)調(diào)度開始的時候就開始運行任務(wù)1,并關(guān)閉延時調(diào)度服務(wù)。其功能是等待任務(wù)2發(fā)出的信號量,在等待信號量的時候掛起自身任務(wù)。為了便于測試,在任務(wù)1中通過函數(shù)SystemClockSave()記錄某些關(guān)鍵步驟的系統(tǒng)時間,用于任務(wù)運行時間的測量。具體實現(xiàn)如下所示:
OS_CREATE_TASK(Task1,0,OS_TASK_STATUS_RDY,-1);
任務(wù)2功能是周期性發(fā)出信號量給任務(wù)1,任務(wù)2每隔10個節(jié)拍后發(fā)送信號量給任務(wù)1,隨后掛起自身任務(wù)。同時,在任務(wù)2中通過函數(shù)SystemClockSave()記錄某些關(guān)鍵步驟的系統(tǒng)時間,用于任務(wù)運行時間的測量。具體實現(xiàn)如下所示:
OS_CREATE_TASK(Task2,1,OS_TASK_STATUS_RDY,10);任務(wù)1與任務(wù)2的具體實現(xiàn)如圖4所示。系統(tǒng)實際運行的狀況如圖5所示。
圖4 任務(wù)1與任務(wù)2的具體實現(xiàn)
圖5 系統(tǒng)運行狀態(tài)圖
任務(wù)2在等待延時服務(wù)的時候釋放CPU控制權(quán),返回要延時的節(jié)拍數(shù)到調(diào)度內(nèi)核中。調(diào)度內(nèi)核把該任務(wù)返回的節(jié)拍數(shù)寫入任務(wù)2的TCB中的變量timeout,在Tick中斷服務(wù)程序中對變量timeout實行減1操作。此時系統(tǒng)沒有任務(wù)運行,系統(tǒng)自動進(jìn)入空閑模式,降低系統(tǒng)的功耗。一旦任務(wù)2 TCB的timeout值減至為零,則內(nèi)核重新調(diào)用任務(wù)2。緊接著任務(wù)2發(fā)送信號量到任務(wù)1,因為任務(wù)1優(yōu)先級高于任務(wù)2,任務(wù)2又被掛起,在調(diào)用OS_SEM_POST()之后實現(xiàn)任務(wù)的切換,任務(wù)1得到優(yōu)先運行。只有在任務(wù)1釋放CPU控制權(quán)的時候,任務(wù)2才得以運行。接下來任務(wù)1與任務(wù)2重復(fù)交替執(zhí)行,系統(tǒng)的正確性得到驗證。
本文提出了一種基于Protothread思想的嵌入式系統(tǒng),提供了一種類似于操作系統(tǒng)的編程方式,支持搶占式調(diào)度,具有代碼量小、容易移植及低功耗管理等特性,使得程序的設(shè)計、維護(hù)和調(diào)試更加便捷。每個任務(wù)利用編譯器__LINE__來記錄阻塞點的行號,實現(xiàn)任務(wù)的阻塞,并返回相應(yīng)狀態(tài)到調(diào)度內(nèi)核中,實現(xiàn)任務(wù)狀態(tài)的切換或阻塞點的重運行。下面舉一個例子說明,物聯(lián)網(wǎng)傳感器采集節(jié)點都集成無線收發(fā)模塊,與后臺系統(tǒng)進(jìn)行數(shù)據(jù)交互,為了保證無線數(shù)據(jù)傳輸?shù)目煽啃?,一般都采用“發(fā)送-應(yīng)答”機(jī)制。在此系統(tǒng)中,如果采用狀態(tài)機(jī)的模型,具體代碼流程如圖6所示。
如果采用本文提供的系統(tǒng)編程模式,上面程序可以修改成如圖7所示。
本文提出的基于Protothread思想的多任務(wù)搶占式系統(tǒng)設(shè)計方案為事件驅(qū)動程序設(shè)計提供了一種有效的處理方法,使得程序的設(shè)計、維護(hù)和調(diào)試更加便捷,對于嵌入式軟件開發(fā)有較大的參考價值。
圖6 基于狀態(tài)機(jī)模型下無線收發(fā)架構(gòu)
圖7 基于本文設(shè)計系統(tǒng)架構(gòu)下的無線收發(fā)架構(gòu)
編者注:本文為期刊縮略版,全文見本刊網(wǎng)站www.mesnet.com.cn。
[1]陳浩杰.面向微小衛(wèi)星的Smart-OSEK OS設(shè)計與實現(xiàn)[D].杭州:浙江大學(xué),2013.
[2]董瑋.面向無線傳感網(wǎng)絡(luò)的嵌入式操作系統(tǒng)設(shè)計[D].杭州:浙江大學(xué),2010.
[3]Dunkels A,Schmidt O,Voigt T,et al.Protothreads:simplifying event-driven programming of memory-constrained embedded systems[C]//Proceedings of the 4th international conference on Embedded networked sensor systems,Acm,2006:29-42.
[4]Dunkels A,Schmidt O.Protothreads-lightweight stackless threads in c[EB/OL].[2014-05].http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.60.2455&rep=rep1&type=pdf.
[5]閆石,馬潮.時間觸發(fā)模式下的Protothreads設(shè)計應(yīng)用[J].單片機(jī)與嵌入式系統(tǒng)應(yīng)用,2009(1):15-17.
[6]羅光平.使用Protothread簡化嵌入式系統(tǒng)中的順序流控制[J].單片機(jī)與嵌入式系統(tǒng)應(yīng)用,2007(11):19-21.