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