樓亮亮,周苗,鮑星合
(1.中國科學院 上海微系統與信息技術研究所 無線傳感網與通信重點實驗室,上海201800;2.上海大學)
Protothread是完全基于事件驅動的操作系統,只在語言層面上做了相應的“封裝”。因此,Protothreads具有一定的局限性[2]。Protothread不支持優先級搶占,阻塞的I/O只允許在主線程函數中調用等。針對上述的幾個問題,本文提出的操作系統是在Protothread基礎上的改進,具有低功耗、易移植、支持多任務切換、時間片調度、搶占式調度及事件同步等特點。由于本系統使用了Protothread的核心思想,這也決定了本系統具有一定的局限,在每個任務中避免與switch語句合用[5]。
本系統主要由調度內核、中斷管理系統及軟中斷系統組成,具體框架如圖1所示。
在本系統中,每個任務都定義與該任務相關的任務控制塊(TCB)。將所有任務控制塊組裝成一個隊列,則每個任務控制塊都對應于任務控制塊隊列中的一個節點,每個任務控制塊中的數據只能由中斷程序或者調度內核來修改。任務控制塊中包含該任務的入口地址、支持時間片調度的服務延時、信號量及當前任務的狀態。為了節省內存,其中信號量和當前任務狀態采用位定義。其數據結構如下所示 :

圖1 系統的架構

本系統支持優先級調度。任務的優先級對應于其任務的控制塊在任務控制塊隊列中的位置,任務控制塊在該隊列中的位置越靠前,該任務就越早被調用,優先級也就越高。
本系統的任務狀態劃分為3種:空閑、就緒及運行。在任何時刻,任務的狀態必定是這3種狀態中的一個。本文涉及的系統與一般嵌入式操作系統(如μC/OS、FreeRTOS等)設計方式不同,本系統中的任何任務都不是無限循環,且都有返回值,任務在阻塞的時候會返回相應的狀態給調度內核,任務的無限循環只能由調度內核來實現。任務的返回值為一個8位有符號數,該值的正數部分0~127留給任務申請延時服務的節拍數,而-128~-1則作為信號量、任務執行結束等其他相應操作的返回值。任務把該值返回到內核后,內核根據該值的數據作出相應操作。
OS_TASK_END_RET是用來指示當前運行的任務執行完所有操作的返回值,內核得到該返回值后會重新運行該任務,實現任務的無限循環;OS_TASK_SEM_RET用來指示當前運行的任務有信號量發出,內核得到該返回值后,根據該值把該任務TCB中的變量sem設置成指定的數據值,且把該任務置為就緒態(如果有其他任務向該任務發出信號量,則執行該任務);OS_TASK_TIME_RET用來指示當前運行的任務申請延時運行服務的返回節拍數(該值為正數),內核得到該返回值后,將其賦予該任務TCB中的變量timeout。如果每個任務TCB中的變量timeout≥0,則該值在Tick中斷中實現減1操作,一旦該值減至為0,內核就會重新運行該TCB對應的任務。
在系統開始調度的時候,調度內核首先檢查pid是否超出了系統所定義的任務總數。如果超過任務總數,說明系統已經執行完所有用戶指定的任務,則調度內核停止調度且執行空閑任務,進入低功耗模式。空閑任務的退出,只有pid數值在事件中斷或Tick中斷中被改變才會實現,一旦退出空閑任務,系統則重新開始調度。
如果pid小于系統所定義的任務總數,則系統開始從TCB隊列中獲取每個TCB中變量status的數值,該變量用于記錄任務狀態。如果任務狀態是就緒態,則加載任務TCB中的入口函數地址到內核中執行。在任務執行的過程中,一旦任務被阻塞或執行結束,任務就會返回相應的數值到調度內核中,調度內根據該返回值,開展相應工作。
具體調度算法流程如圖2所示。

圖2 調度算法流程圖
為了支持一級任務優先的搶占式調度功能,本系統采用了軟中斷模式,利用中斷來自動壓棧與出棧,把高優先級的任務安排在軟中斷服務程序中運行。由于軟件中斷優先級比較低,系統運行基本不受影響。例如在Tick中斷中,有高優先級任務就緒,如果直接在Tick中斷中運行,由于任務運行帶來時間的不確定性,會嚴重影響Tick的定時精度。而采用軟中斷的模式,由于中斷優先級比較低,在其服務程序中運行高優先級的任務時,其他中斷(如Tick中斷)不會受到影響。
這將帶來兩個方面的好處:①如果高優先級任務在中斷服務中直接執行,將會影響Tick定時器的定時精度或者是其他中斷的響應時間。②利用軟中斷自動壓棧出棧功能,可以減少上下文切換,降低RAM的需要,從而提高了效率,降低了系統的整體功耗。
本系統和其他內核一樣,需要時鐘節拍來實現時間片調度和延時服務。在本系統中實現上述服務的函數為OS_TIME_DLY()。調用該函數之后,該任務會返回延時的節拍數到調度內核中,內核會把返回的節拍數寫入該任務TCB中的變量timeout,內核實現一次調度,執行下一個就緒態任務。具體實現如下所示:
#define OS_TIME_DLY(ticks)do{_lc=__LINE__;return ticks;}while(0);case__LINE__:
為了實現時間片調度,需要在Tick中斷中調用OS_TIME_UPDATE(),來更新每個任務TCB中變量timeout的數據值。如果該變量數據≥0,則在每次中斷中進行減1操作,否則不做任何處理。當任務TCB中變量timeout減至0,則把對應的任務置為就緒態,內核在調度的時候就會執行就緒態的任務,從而實現時間片調度和延時服務功能。該函數的具體流程如圖3(a)所示。
本系統中事件同步采用了信號量設計方式,涉及到該項服務的有兩個函數:等待信號量與發送信號量。若一個任務等待一個信號量,則調用OS_SEM_PEND()阻塞該任務并返回OS_TASK_SEM_RET到調度內核,調度內核根據該返回值把相應狀態寫入該任務TCB中的變量sem,并把該任務置為就緒狀態。具體實現如下所示:
#define OS_SEM_PEND()do{_lc=__LINE__;return OS_TASK_SEM_RET ;}while(0);case__LINE__:
發送一個信號量函數,實現相對等待信號量比較復雜,需要涉及到任務的切換。當某個任務或者中斷中調用OS_SEM_POST()時,該函數在執行結束后返回要發送信號量的任務ID號到內核中,調度內核會判斷該任務TCB中R變量sem是否在等待該信號量。如果是,則執行任務切換,即開啟軟中斷。涉及到的函數如下:
INT8S OS_SEM_POST(INT8S (*ptask)(void))
其中:ptask為要發送信號量到的任務名。基本流程如圖3(b)所示。
本文涉及的系統是在IAR FOR MSP430 V5.30.1執行,采用的編譯模式為:Release、優化等級為Level high balanced。表1略——編者注。
綜上所述,本系統所帶來的RAM額外消耗可由以下式計算得出:
RAMoverhead=任務數×5字節+1字節

圖3 時間片調度算法流程與信號量發送流程
本系統在MSP430F149平臺上驗證其正確性及可靠性。在實驗中采用定時器A作為系統的Tick時鐘,為了測量每個任務的運行時間及切換時間,本系統中Tick定時周期為16μs。在實際應用中,用戶可以根據系統的實際應用場合而定,系統Tick周期越短則系統負荷越大。由于MSP430系統微處理器沒有相關軟中斷指令,故采用定時器B作為軟中斷來實現任務的切換,其優先級相對定時器A與其他中斷來說比較低。
為了驗證其正確性,本文在該系統平臺上建立了兩個任務。同時系統會自動增加一個空閑任務,兩個任務都不需要執行的時候,系統就會自動進入空閑模式(即休眠模式),降低了功耗。
任務1優先級最高,創建的時候設置其為就緒狀態,所以在任務調度開始的時候就開始運行任務1,并關閉延時調度服務。其功能是等待任務2發出的信號量,在等待信號量的時候掛起自身任務。為了便于測試,在任務1中通過函數SystemClockSave()記錄某些關鍵步驟的系統時間,用于任務運行時間的測量。具體實現如下所示:
OS_CREATE_TASK(Task1,0,OS_TASK_STATUS_RDY,-1);
任務2功能是周期性發出信號量給任務1,任務2每隔10個節拍后發送信號量給任務1,隨后掛起自身任務。同時,在任務2中通過函數SystemClockSave()記錄某些關鍵步驟的系統時間,用于任務運行時間的測量。具體實現如下所示:
OS_CREATE_TASK(Task2,1,OS_TASK_STATUS_RDY,10);任務1與任務2的具體實現如圖4所示。系統實際運行的狀況如圖5所示。

圖4 任務1與任務2的具體實現

圖5 系統運行狀態圖
任務2在等待延時服務的時候釋放CPU控制權,返回要延時的節拍數到調度內核中。調度內核把該任務返回的節拍數寫入任務2的TCB中的變量timeout,在Tick中斷服務程序中對變量timeout實行減1操作。此時系統沒有任務運行,系統自動進入空閑模式,降低系統的功耗。一旦任務2 TCB的timeout值減至為零,則內核重新調用任務2。緊接著任務2發送信號量到任務1,因為任務1優先級高于任務2,任務2又被掛起,在調用OS_SEM_POST()之后實現任務的切換,任務1得到優先運行。只有在任務1釋放CPU控制權的時候,任務2才得以運行。接下來任務1與任務2重復交替執行,系統的正確性得到驗證。
本文提出了一種基于Protothread思想的嵌入式系統,提供了一種類似于操作系統的編程方式,支持搶占式調度,具有代碼量小、容易移植及低功耗管理等特性,使得程序的設計、維護和調試更加便捷。每個任務利用編譯器__LINE__來記錄阻塞點的行號,實現任務的阻塞,并返回相應狀態到調度內核中,實現任務狀態的切換或阻塞點的重運行。下面舉一個例子說明,物聯網傳感器采集節點都集成無線收發模塊,與后臺系統進行數據交互,為了保證無線數據傳輸的可靠性,一般都采用“發送-應答”機制。在此系統中,如果采用狀態機的模型,具體代碼流程如圖6所示。
如果采用本文提供的系統編程模式,上面程序可以修改成如圖7所示。
本文提出的基于Protothread思想的多任務搶占式系統設計方案為事件驅動程序設計提供了一種有效的處理方法,使得程序的設計、維護和調試更加便捷,對于嵌入式軟件開發有較大的參考價值。

圖6 基于狀態機模型下無線收發架構

圖7 基于本文設計系統架構下的無線收發架構
編者注:本文為期刊縮略版,全文見本刊網站www.mesnet.com.cn。
[1]陳浩杰.面向微小衛星的Smart-OSEK OS設計與實現[D].杭州:浙江大學,2013.
[2]董瑋.面向無線傳感網絡的嵌入式操作系統設計[D].杭州:浙江大學,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]閆石,馬潮.時間觸發模式下的Protothreads設計應用[J].單片機與嵌入式系統應用,2009(1):15-17.
[6]羅光平.使用Protothread簡化嵌入式系統中的順序流控制[J].單片機與嵌入式系統應用,2007(11):19-21.