嵌入式實時操作系統µC/OS-II內核結構簡介

      本文介紹了嵌入式實時操作系統的概念和特點,並簡要介紹了嵌入式操作系統µC/OS-II的內核結構。

            關鍵詞    嵌入式實時操作系統,µC/OS-II,中斷,任務,優先級

 <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

1           嵌入式實時操作系統概述[1]

所謂嵌入式系統是以應用爲中心,以計算機技術爲基礎,軟硬件可裁減,從而能夠適應實際應用中對功能、可靠性、成本、體積、功耗等嚴格要求的專用計算機系統。它一般由嵌入式微處理器、外圍硬件設備、嵌入式操作系統以及用戶的應用軟件等四個部分組成,用於實現對其他設備的控制、監視或管理等功能。在大型嵌入式應用系統中,爲了使嵌入式開發更方便、快捷,需要具備一種穩定、安全的軟件模塊集合,用以管理存儲器分配、中斷處理、任務間通信和定時器響應,以及提供多任務處理等,即嵌入式操作系統。嵌入式操作系統的引入大大提高了嵌入式系統的功能,方便了應用軟件的設計,但同時也佔用了寶貴的嵌入式系統資源。嵌入式操作系統常常有實時要求,所以嵌入式操作系統往往又是“實時操作系統”。早期的嵌入式系統幾乎都用於控制目的,從而或多或少都有些實時要求,所以從前“嵌入式操作系統”實際上是“實時操作系統”的代名詞。近年來,由於手持式計算機和掌上電腦等設備的出現,也有了許多不帶實時要求的嵌入式操作系統。另一方面,由於CPU速度的提高,一些原先以爲是“實時”的反應速度現在已經很普遍了。這樣,一些原先需要在“實時”操作系統上才能實現的應用,現在已不難在常規的操作系統上實現。在這樣的背景下,“嵌入式操作系統”和“實時操作系統”就成了不同的概念和名詞。而實時操作系統能及時響應外部事件的請求,在規定的時間內完成對該事件的處理,並控制所有實時任務協調一致地運行,具有獨立性、及時性、可靠性的特點。顧名思義,嵌入式實時操作系統則是在綜合了以上的兩種操作系統的特點之後形成的,嵌入式實時操作系統沒有一般的計算機操作系統的文件管理等龐大內容,一般也沒有內存管理,它所擁有的是實時操作系統中最重要的內容,即多任務實時調度和任務的定時、同步操作。其二進制代碼的大小通常爲幾KB到幾十KB,是純粹爲嵌入式應用而設計的,具有很短的任務切換時間和很高的實時響應速度。而嵌入式實時操作系統的核心是實時多任務內核。

2           嵌入式實時操作系統µC/OS-II簡介[2]

µC/OS-II是著名的源代碼公開的實時內核,是一個完整的,可移植、固化、裁剪的佔先式實時多任務內核。µC/OS-II是用ANSI C編寫的,包含一小部分與微處理器類型相關的彙編語言代碼,使之可供不同架構的微處理器使用。雖然µC/OS-II是在PC機上開發和測試的,但µC/OS-II的實際對象是嵌入式系統,並且很容易移植到不同架構的微處理器上。至今,從8位到64位,µC/OS-II已在超過40中不同架構的微處理器上運行。

3           嵌入式實時操作系統µC/OS-II內核結構

3.1 臨界區(Critical Sections),OS_ENTER_CRITICAL()OS_EXIT_CRITICAL()

同其他內核一樣,µC/OS-II爲了處理臨界區代碼,必須關中斷,處理完畢後再開中斷。關中斷使得µC/OS-II能夠避免同時有其他任務或中斷服務進入臨界區代碼。關中斷的時間是實時內核開發商應提供的最重要的指標之一,因爲這個指標影響用戶系統對實時事件的響應特性。µC/OS-II努力使關中斷時間降至最短,但就使用µC/OS-II而言,關中斷的時間在很大程度上取決於微處理器的結構以及C編譯器所生成的代碼質量。

微處理器一般都有關中斷和開中斷指令,用戶使用的C編譯器必須具有某種機制,能夠在C源代碼中直接實現關中斷/開中斷操作。有些C編譯器允許在用戶的C源代碼中嵌入彙編語言的語句,使得關中斷/開中斷很容易實現;而有些C編譯器把從C語言中關中斷/開中斷的操作放在語言的擴展部分,從而直接從C語言中可以關中斷/開中斷。

µC/OS-II定義了2個宏來關中斷和開中斷,以便避免不同C編譯器廠商使用不同的方法來處理關中斷和開中斷。µC/OS-II中的這2個宏分別是OS_ENTER_CRITICAL()OS_EXIT_CRITICAL()。因爲這2個宏的定義取決於使用的微處理器,故在文件OS_CPU.H中可以找到相應的宏定義。每種微處理器都有自己的OS_CPU.H文件。

OS_ENTER_CRITICAL()OS_EXIT_CRITICAL()總是成對使用的,把臨界區代碼封裝起來,如以下代碼所示:

{

    ……

    ……

    OS_ENTER_CRITICAL();

    /* µC/OS-II臨界區代碼 */

    OS_EXIT_CRITICAL();

    ……

    ……

}

OS_ENTER_CRITICAL()OS_EXIT_CRITICAL()也可以用來保護應用程序中的臨界區代碼。

3.2 任務(Tasks

任務通常是一個無限循環,但是當任務完成後,任務可以自我刪除。µC/OS-II可以管理多達64個任務,但是µC/OS-II的作者建議用戶不要使用優先級爲0123的任務,以及優先級爲OS_LOWEST_PRIO-3OS_LOWEST_PRIO-2 OS_LOWEST_PRIO-1 OS_LOWEST_PRIO的任務,因爲在未來的µC/OS-II版本中可能會用到這些任務。因此,如果遵循作者的建議,不使用以上優先級最高的4個任務和優先級最低的4個任務,則用戶最多可以有56個自己的任務。

3.3 任務狀態(Task States

下圖是µC/OS-II控制下的任務狀態轉換圖[3]。在任一給定的時刻,任務的狀態應爲以下5種狀態之一。

<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />

睡眠態(DORMANT)——指任務駐留在程序空間,還沒有交給µC/OS-II來管理。把任務交給µC/OS-II,是通過調用下述2個函數之一:OSTaskCreate()OSTaskCreateExt()來實現的。這些調用只是用於告訴µC/OS-II,任務的起始地址在哪裏;任務建立時,用戶給任務賦予的優先級是多少;任務要使用多少棧空間等。

就緒態(READY)——任務一旦建立,這個任務就進入了就緒態,準備運行。任務的建立可以是在多任務運行開始之前,也可以動態地由一個運行着地任務建立。如果多任務已經啓動,且一個任務是被另一個任務建立的,而新建立的任務優先級高於建立它的任務的優先級,則這個剛剛建立的任務將立即得到CPU的使用權。一個任務可以通過調用OSTaskDel()返回到睡眠態,或通過調用該函數讓另一個任務進入睡眠態。

運行態(RUNNING)——調用OSStart()可以啓動多任務。OSStart()函數只能在啓動時調用一次,該函數運行用戶初始化代碼中已經建立的、進入就緒態的優先級最高的任務。優先級最高的任務就這樣進入了運行態。任何時刻只能有一個任務處於運行態。就緒的任務只有當所有優先級高於這個任務的任務都轉爲等待狀態,或者是被刪除了,才能進入運行態。

等待狀態(WAITING)——正在運行的任務可以通過調用以下2個函數之一:OSTimeDly()OSTimeDlyHMSM(),將自身延遲一段時間。這個任務於是進入等待狀態,一直到函數中定義的延遲時間到。這2個函數會立即強制執行任務切換,讓下一個優先級最高的、並進入了就緒態的任務運行。等待的時間過去以後,系統服務函數OSTimeTick()使延遲了的任務進入就緒態。而正在運行的任務可能需要等待某一事件的發生,可以通過調用以下函數之一實現:OSFlagPend()OSSemPend()OSMutexPend()OSMboxPend()OSQPend()。如果該事件並未發生,調用上述函數的任務就進入了等待狀態,直到等待的事件發生了。當任務因等待事件而被掛起時,下一個優先級最高的任務立即得到了CPU的使用權。當事件發生了或等待超時使,被掛起的任務就進入就緒態。事件發生的報告可能來自另一個任務,也可能來自中斷服務子程序。

中斷服務態(ISR)——正在運行的任務是可以被中斷的,除非該任務將中斷關閉,或者µC/OS-II將中斷關閉。被中斷了的任務於是進入了中斷服務態。相應中斷時,正在執行的任務被掛起,中斷服務子程序得到了CPU的使用權。中斷服務子程序可能會報告一個或多個事件的發生,而使一個或多個任務進入就緒態。在這種情況下,從中斷服務子程序返回之前,µC/OS-II要判定被中斷的任務是否還是就緒態任務中優先級最高的。如果中斷服務子程序使另一個優先級更高的任務進入就緒態,則新進入就緒態的這個優先級更高的任務將得以運行;否則,原來被衆多拉的任務將繼續運行。

當所有的任務都在等待事件的發生或等待延遲時間的結束時,µC/OS-II執行被稱爲空閒任務(idle task)的內部任務,即OSTaskIdle()

3.4 任務控制塊(Task Control Blocks

一旦任務建立,一個任務控制塊OS_TCB就被賦值。任務控制塊是一個數據結構,當任務的CPU使用權被剝奪是,µC/OS-II用它保存該任務的狀態。當任務重新得到CPU使用權時,任務控制塊能確保任務從當時被中斷的那一點絲毫不差地繼續執行。OS_TCB全部駐留在RAM中。

3.5 就緒表(Ready List

每個任務被賦予不同的優先級等級,從0到最低優先級OS_LOWEST_PRIO,包括0OS_LOWEST_PRIO在內。當µC/OS-II初始化時,最低優先級OS_LOWEST_PRIO總是被賦給空閒任務。

每個就緒的任務都放在就緒表中,就緒表中有2個變量,OSRdyGrpOSRdyTbl[]。在OSRdyGrp中,任務按優先級分組,8個任務爲一組。OSRdyGrp中的每一位表示8組任務中每一組是否有進入就緒態的任務。任務進入就緒態時,就緒表OSRdyTbl[]中相應元素的相應位也置爲1OSRdyGrpOSRdyTbl[]之間的關係見下圖[3]

就緒表OSRdyTbl[]數組的大小取決於OS_LOWEST_PRIO。當應用程序中任務數目比較少時,這種安排可以減小OS_LOWEST_PRIO的值,可以降低µC/OS-IIRAM的需求量。

爲確定下一次該哪個優先級的任務運行了,µC/OS-II中的調度器總是將最低優先級的任務在就緒表中相應字節的相應位置1

從上圖可以看出,任務優先級的低3位用於確定任務在總就緒表OSRdyTbl[]中的所在位。接下去的3位用於確定是在OSRdyTbl[]數組的第幾個元素。

3.5 任務調度(Task Scheduling

µC/OS-II總是運行進入就緒態任務中優先級最高的任務。確定哪個任務優先級最高,下面該哪個任務運行了,這一工作是由調度器(scheduler)完成的。任務級的調度是由函數OS_Sched()完成的。中斷級的調度是由另一個函數OSIntExt()完成的。µC/OS-II任務調度的執行時間是常數,與應用程序建立了多少個任務沒有關係。

任務切換很簡單,由以下2步完成:將被掛起任務的處理器寄存器壓入堆棧;然後將較高優先級任務的寄存器值從堆棧中恢復到寄存器中。在µC/OS-II中,就緒任務的堆棧結構總是看起來跟剛剛發生過中斷一樣,所有處理器的寄存器都保存在堆棧中。換句話說,µC/OS-II運行就緒態的任務所要做的一切,只是恢復所有的CPU寄存器並運行中斷返回指令。爲了做任務切換,運行OS_TASK_SW(),人爲模仿了一次中斷。多數微處理器由軟中斷指令或者指令陷阱來實現上述操作。中斷服務子程序或陷阱處理,也稱做異常處理,必須給彙編語言函數OSCtxSw()提供中斷向量。OSCtxSw()除了需要OS_TCBHighRdy指向即將被掛起的任務,還需要讓當前任務控制塊OSTCBCur指向即將被掛起的任務。

OS_Sched()的所有代碼都屬於臨界區代碼。在尋找進入就緒態的優先級最高的任務過程中,爲防止中斷服務子程序把一個或幾個任務的就緒位置位,中斷是關閉的。爲縮短切換時間,OS_Sched()全部代碼都可以用彙編語言寫。爲增加可讀性、可移植性及將彙編語言代碼最少化,OS_Sched()是用C語言編寫的。

3.6 給調度器上鎖和開鎖(Locking and Unlocking the Scheduler

給調度器上鎖函數OSSchedLock()用於禁止任務調度,直到任務完成後,調用給調度器開鎖函數OSSchedUnlock()爲止。調用OSSchedLock()的任務將保持對CPU的使用權,即使有個優先級更高的任務進入了就緒態。此時,中斷仍然是可以識別的,中斷服務也能得到(假設此時中斷是開着的)。OSSchedLock()OSSchedUnlock()必須成對的使用。變量OSLockNesting跟蹤OSSchedLock()函數被調用的次數,以允許嵌套的函數包含臨界區代碼,這段代碼其他任務不得干預。µC/OS-II允許嵌套深度達255層。當OSLockNesting=0時,任務調度重新得到允許。函數OSSchedLock()OSSchedUnlock()的使用要非常謹慎,因爲它們影響到了µC/OS-II對任務的正常管理。調用OSSchedLock()之後,用戶應用程序不得調用可能會使當前任務掛起的系統功能函數。也就是說,用戶應用程序不得調用OSFlagPend()OSMboxPend()OSMutexPend()OSQPend()OSSemPend()OSTaskSuspend(OS_PRIO_SELF)OSTimeDly()或者OSTimeDlyHMSM(),直到OSLockNesting0爲止。因爲OSSchedLock()給調度器上了鎖,不讓其他任務運行,用戶鎖住了系統。

3.7 空閒任務(Idle Task

µC/OS-II總要建立一個空閒任務(idle task),這個任務在沒有其他任務進入就緒態使投入運行。這個空閒任務(OSTaskIdle())永遠被設置爲最低優先級,即OS_LOWEST_PRIO。空閒任務不可能被應用軟件刪除。

3.8 統計任務(Statistics Task

µC/OS-II有一個統計運行時間的任務,叫做OSTaskStat()。如果將系統配置常數OS_TASK_STAT_EN設置爲1,這個任務就會建立。一旦得到了允許,OSTaskStat()每秒運行1次,計算當前的CPU利用率。換句話說,OSTaskStat()告訴用戶應用程序使用了多少CPU時間,用百分比表示。這個值放在一個有符號8位整數OSCPUUsage中,精確度是1%。如果應用程序打算使用統計任務,那麼必須在初始化時建立唯一的任務中調用統計任務初始化函數OSStatInit()。換句話說,在調用系統啓動函數OSStart()之前,用戶初始代碼中必須建立一個任務,在這個任務中調用系統統計任務初始化函數OSStatInit(),然後再建立應用程序中的其他任務。

3.9 µC/OS-II中的中斷(Interrupts under µC/OS-II

µC/OS-II中,中斷服務子程序要用彙編語言來編寫。然而,如果用戶使用的C語言編譯器支持在線(in-line)彙編語言,則可以直接將中斷服務子程序代碼放在C語言的源文件中。µC/OS-II的中斷服務過程大致如下:

1)        中斷到來,但還不能被CPU識別。也許是因爲中斷被µC/OS-II或用戶應用程序關了,或者是因爲CPU還沒執行完當前指令。

2)        一旦CPU響應了這個中斷,CPU的中斷向量被裝載,跳轉到中斷服務子程序。

3)        中斷服務子程序保存CPU的全部寄存器。

4)        保存完CPU寄存器之後,中斷服務子程序通知µC/OS-II進入中斷服務子程序。做法是調用OSIntEnter(),或者給OSIntNesting1。還應該將堆棧指針保存到當前任務控制塊OS_TCB中。

5)        用戶中斷服務代碼開始執行。中斷服務所做的事應儘可能的少,應把大部分工作留給任務去做。

6)        中斷服務完成後,必須調用OSIntExit(),通知µC/OS-II退出中斷服務。

7)        恢復CPU的寄存器,中斷返回。

3.10 時鐘節拍(Clock Tick

µC/OS-II需要提供週期性的信號源,用於實現時間延時和確認超時。節拍率應爲1020/秒,或者說10100Hz。時鐘節拍率越高,系統的額外負荷就越重。時鐘節拍的實際頻率取決於應用程序的精度。時鐘節拍源可以是專門的硬件定時器,也可以是來自50/60Hz交流電源的信號。必須在多任務系統啓動之後,也就是在調用OSStart()之後,再開啓時鐘節拍器。換句話說,調用OSStart()之後應做的第一件事是初始化定時器中斷。通常容易犯的錯誤是,將允許時鐘節拍器中斷放在系統初始化函數OSInit()之後,在啓動多任務系統啓動函數OSStart()之前。µC/OS-II中的時鐘節拍服務是通過在中斷服務子程序中調用OSTimeTick()實現的。OSTimeTick()跟蹤所有任務的定時器以及超時時限,時鐘節拍中斷服務子程序的代碼必須用彙編語言編寫,因爲在C語言中不能直接處理CPU的寄存器。

3.11 µC/OS-II初始化(µC/OS-II Initialization

在調用µC/OS-II的任何其他服務之前,µC/OS-II要求首先調用系統初始化函數OSInit()OSInit()初始化µC/OS-II所有的變量和數據結構。OSInit()建立空閒任務OSTaskIdle(),這個任務總是處於就緒態。空閒任務OSTaskIdle()的優先級總是設成最低,即OS_LOWEST_PRIO。如果統計任務允許OS_TASK_STAT_EN和任務建立擴展都設爲1,則OSInit()還須建立統計任務OS_TaskStat(),並且使其進入就緒態。OS_TaskStat的優先級總是設爲OS_LOWEST_PRIO-1

3.12 µC/OS-II的啓動Starting µC/OS-II

多任務的啓動是通過調用OSStart()實現的。然而在啓動µC/OS-II之前必須建立至少一個應用任務當調用OSStart()OSStart()從任務就緒表中找出用戶建立的優先級最高的任務的任務控制塊接着OSStart()調用高優先級就緒任務啓動函數OSStartHighRdy()函數後者將任務棧中保存的值彈回到CPU寄存器中然後執行一條中斷返回指令,中斷返回指令強制執行該任務代碼

4          結束語

以上簡要介紹了嵌入式實時操作系統µC/OS-II的內核結構,可以看出µC/OS-II具備一個嵌入式實時操作系統內核所必須的基本功能和良好的可移植性,目前也已經在嵌入式系統領域得到了廣泛的應用。

 

     

[1]  馬忠梅 李善平 康慨 葉楠,《ARM&Linux嵌入式系統教程》,北京航空航天大學出版社,2004

[2]  Jean J. Labrosse,邵貝貝 等譯,《嵌入式實時操作系統µC/OS-II2)》,北京航空航天大學出版社,2003

[3]  Jean J. Labrosse,《MicroC/OS-II The Real-Time Kernel Second Edition》,CMP Books 2002

A Brief Introduction to Embedded RTOS Kernel µC/OS-II

Abstract

This paper introduces the concepts and features of embedded real-time operation system and the embedded RTOS kernel µC/OS-II architecture.

Keywords

embedded RTOS, µC/OS-II, interrupt, task, priority

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章