如果,你最近關注一些嵌入式招聘職位描述,你可能會經常看到看到使用過uCOS、Vxworks、QNX等RTOS者優先。
隨便打開一個20K的嵌入式開發工作職責:
你會發現熟悉RTOS的開發、移植、剪裁真的很吃香!
今天,我們就來介紹一下實時操作系統UCOS-II。
一、嵌入式操作系統概覽
嵌入式操作系統的主要好處就是屏蔽了底層硬件的差別,給上層應用提供統一的接口,并管理進程調度和資源(如CPU時間、內存)分配等。并且可以充分利用硬件資源,如在單任務時(大循環結構,如大部分51程序)遇到delay函數時,CPU在空轉。而在多任務系統,遇到delay或需等待資源時系統會自動運行下一個任務,等條件滿足再回來運行先前的任務,這樣就充分利用了CPU,提高了效率。
uC/OS操作系統與裸機程序的最大不同點就在于uC/OS有任務調度,可以根據任務的重要程度(優先級)優先執行重要的任務,從而確保能及時處理最重要的數據。(所以對于一個系統有必要使用OS的判斷是能否劃分一個個的任務,并且各任務間的耦合很小)可以思考下裸機程序中斷的時候發生的過程。利用堆棧可以很自由的在A、B中切換,如果切換足夠快,A、B看以來好像同時在執行,這就是并行,A、B就是任務。如果這個切換操作放到定時器函數中來做,就可以嚴格按照時間來切換。另外,各個任務之間有存在一定的關系,有邏輯上的先后等,必須引進全局的結構體、變量來標記一些信息,全局的這些數據是不會被釋放的,所以所有的任務可以去通過讀、寫這些數據來實現各個程序塊交流信息,實現所謂的同步、互斥。這就是操作系統的原理,而這些不同的通信方式按功能細分就成事件管理、內存管理等。
二、ucos的運行概覽
首先是主函數,然后是OSInit(),這個函數就是對那些全局的數據結構初始化,建立希望的鏈表等數據結構,為后面全局變量通信做好準備,并且創建了1-2個系統任務(空閑任務必須,為了不讓操作系統返回。統計任務可選),而所謂的創建任務OSTaskCreate就是把一個函數的函數地址、自己的棧建立聯系、優先級、任務控制塊等弄好,為任務切換做好準備。設置好定時切換的相關信息類似定時器,按照節拍在中斷中進行任務切換判斷(這個節拍是給延時函數提供計時基準,一個任務的延時時間到或等待的資源滿足而進入就緒表就會檢查優先級看是否可以執行),可以的話就發生切換,這個時候還沒有開啟開關,所以等任務創建完成后,啟動多任務函數OSStart(),這個函數是讓SP指到其中的一個棧,然后出棧就跳到一個任務函數里去了,接下來就是正常的任務運行了。
對于操作系統,主要是任務怎么釋放CPU(延時、中斷、等待資源),其他的任務怎么獲得CPU(進入就緒表),如何找到某個任務(優先級及任務控制列表)。
三、ucos各部分介紹
μC/OS-II的各種服務都以任務的形式來出現的。在μC/OS-II中,每個任務都有一個唯一的優先級。它是基于優先級可剝奪型內核,適合應用在對實時性要求較高的地方。
3.1 μC/OS-II的任務
μC/OS-II的核心部分就是它的任務,它也是通過任務來對不同事件進行響應和處理的。從代碼上來看,μC/OS-II的任務一般為如下形式(C語言描述,后同):
void uCOSTask(void *p)
{
while(1)
{
任務具體的功能;
}
}
創建任務的函數有個是OSTaskCreate (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio),OSTaskCreate()需要四個參數:task是任務代碼的指針,pdata是當任務開始執行時傳遞給任務的參數的指針,ptos是分配給任務的堆棧的棧頂指針,prio是分配給任務的優先級。
μC/OS-II的任務是在內存中來看,任務由三個部分構成:任務的代碼部分、任務堆棧和任務控制塊。其中任務控制塊保存任務的屬性;任務堆棧在任務進行切換時保存任務運行的環境;任務代碼部分就是宏觀上看到的C語言代碼。
嵌入式設備中一般只有一個處理器,所以在某一具體的時刻只能有一個任務占用處理器。μC/OS-II的任務一共有5種狀態:睡眠、就緒、運行、等待和中斷服務。
3.2 任務控制塊
μC/OS-II中參與調度和管理的最小單位是任務。而任務是通過任務控制塊的形式管理的。任務控制塊是一個結構體,它包含了任務的堆棧信息,任務控制塊的指針,前一個任務控制塊和后一個任務控制塊的指針(利用優先級一個個查找是否是要找的任務,所以優先級是唯一的),任務的優先級(根據優先級查找到任務控制塊,從而就找到該任務),任務需要等待的時間(任務延時的時候時鐘節拍中斷來的時候會對等待時間做減,為零的時候就放入就緒表,查看一下是否需要切換)等信息。
任務控制塊包含了除了指向任務代碼的所有信息。而任務的代碼地址在任務運行時是怎么獲得的呢?其實,任務代碼的地址是通過任務的堆棧儲存的。
3.3 任務堆棧
任務在創建時候,必須指明該任務的堆棧。任務的堆棧大小由用戶根據實際情況自行定義。μC/OS-II的堆棧實際上是一個連續的內存塊,任務在創建的時候,由函數OSTaskCreate()將任務的代碼和用戶為任務定義的堆棧聯系起來。由于堆棧按照增長方向可以分為兩種類型,故在創建任務的時候調用的堆棧初始化函數實際上也跟微處理器類型有關的。故這些代碼也是移植時需要修改的。
3.4系統任務
μC/OS-II提供了兩個系統任務:空閑任務和統計任務。其中空閑任務是必要的。因為在某一時刻可能所有的用戶任務都不處于就緒狀態,這樣微處理器會因為沒有任何任務運行造成系統崩潰。
3.5 臨界區
μC/OS-II還有一個臨界的概念,所謂臨界區,就是一段特殊的代碼。在這段代碼內不允許中斷的響應,以此來保證這段代碼的原子性。臨界代碼段通過調用開關中斷兩個宏來實現的。
3.6 μC/OS-II任務的管理
3.6.1 對就緒任務的管理
μC/OS-II定義了一個就緒表的數據結構,跟普通的數組非常像(也就是一維數組),但是被賦予了特殊的意義。就緒表中每一位表示一個優先級的任務是否處于就緒狀態。而每一位的下標則表示任務的優先級。通過特殊的數據結構和意義,就緒任務的管理效率很高。
任務就緒表是由一個OSRdyTbl數組表示,數組大小(OS_RDY_TBL_SIZE)由最低優先級(OS_LOWEST_PRIO)確定, 這樣可以減少不必要的空間浪費,節約RAM資源。OSRdyTbl[]是INT8U 類型數組,每一個元素占8位。每一位表示一個優先級狀態(1為就緒,0則未就緒)。8個元素則可以表示64個優先級(8*8=64)。為加速就續表的查找,Labrosse把每個OSRdyTbl元素劃為每一優先級組,8個元素則有8個優先級組,它定義了一個INT8U類型的8位變量OSRdyGrp ,OSRdyGrp的每一位對應每個優先級組。如下圖:
任務優先級的第三位用于確定任務在osRdyTb1中在元素的第幾位,接著的三位用于指定是第幾個元素。
假設優先級31的任務第一個加入了就緒任務表,此時OSRdyGrp和OSRdyTbl的情況:
OSRdyGrp的第3位為1,表示第3優先級組有就緒任務。OSRdyTbl的第7位為1,表示第31優先級的任務被就緒。
用此可以使任務加入或脫離就緒表。調度的時候即是查找此表,找出最高的優先級,從而找到任務控制塊,執行該任務。
3.6.2 任務的創建、掛起和其他操作
μC/OS-II提供了兩個函數可以創建任務,它們是OSTaskCreate()和OSTaskCreateExt(),任務在創建之后也可以掛起或者恢復,這同樣要使用μC/OS-II提供的系統函數。掛起任務使用函數OSTsakSuspend(),恢復被掛起的任務使用函數OSTaskResume()。μC/OS-II還提供了任務的刪除,優先級的修改,查詢任務信息等其他功能的函數。
3.6.3 任務的調度
μC/OS-II任務的調度是由調度器完成的。所謂調度器實際上是一個函數OSShed();此函數通過搜索任務就緒表來獲得最高優先級的就緒任務,在由該任務的優先級來獲得任務的控制塊再來實現任務的切換。
任務的調度不是任何時刻都進行的,而是有時機的。μC/OS-II任務當有以下情況發生時將產生一次任務調度:
● 創建了新任務,并在就緒表中進行了登記
● 有任務被刪除
● 有處于等待的任務被喚醒
● 中斷退出的時候
● 正在運行的任務等待某事件而進入等待狀態
● 正在運行的任務自愿放棄微處理器占有權而等待一段時間
3.6.4任務的初始化和啟動
μC/OS-II中定義了大量的全局變量和數據結構。在μC/OS-II運行以前需要對這些全局變量和數據結構進行初始化。為了完成μC/OS-II的初始化,系統提供了初始化函數OSInit()。μC/OS-II的啟動也是通過系統提供的函數OSStart()來實現的。OSStart()在判斷系統沒有在運行后來獲得就緒表中最高優先級的就緒任務,并調用函數OSStartHighRdy()來啟動系統。
3.6.5 中斷和時鐘
實時系統為了能夠響應異步事件,通常會采用中斷。μC/OS-II也采用了中斷來響應外部事件。μC/OS-II處理中斷過程大致如下:當系統開中斷時,系統接收到中斷然后找到中斷服務程序的入口地址執行中斷,執行完成后退出中斷。這里要提到的一點是,當要退出中斷時,系統會查找就緒表是否有比處于中斷服務狀態任務的優先級更高的任務進入就緒狀態。如果有將會一發一次調度,否則返回被中斷的任務繼續運行。關于中斷的一些細節在后面的移植的部分還會討論。
在所有的中斷源中最重要的一個就是時鐘中斷,它為系統提供時間服務以此來實現任務的延時。
3.6.6 任務間的通信
對于一個完整的嵌入式操作系統來說,任務間的通信機制必不可少。μC/OS-II提供了相應的數據結構和機制來實現任務之間的同步和通信。
在ucos II 里任務間通信可以采用以下幾種方式:
1. 共享全局變量,這是最快捷有效的方式,實現這種通信可以采用以下兩種方式:一是利用宏OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()來關閉中斷和打開中斷,二是利用函數OSSchedLock()和OSSchedUnlock()對μC/OS-II中的任務調度函數上鎖和開鎖.
2. 使用信號量
3. 使用郵箱
4. 使用消息隊列
創建一個任務需要給這個任務分配一個任務控制塊,這個任務控制塊存儲著關于這個任務的重要信息。那么,事件控制塊就好比任務里的任務控制塊。它存儲著這個事件的重要信息,我們說創建一個事件(信號,郵箱,消息隊列),其本質的過程就是初始化這個事件控制塊。多個任務可以同時等待同一個事件的發生。在這種情況下,當該事件發生后,所有等待該事件的任務中,優先級最高的任務得到了該事件并進入就緒狀態,準備執行。
事件控制塊是一個數據結構,其定義如下:
typedef struct {
void *OSEventPtr; /* 指向消息或者消息隊列的指針 */
INT8U OSEventTbl[OS_EVENT_TBL_SIZE]; /* 等待任務列表 */
INT16U OSEventCnt; /* 計數器(當事件是信號量時) */
INT8U OSEventType; /* 時間類型 */
INT8U OSEventGrp; /* 等待任務所在的組 */
} OS_EVENT;
.OSEventPtr指針,只有在所定義的事件是郵箱或者消息隊列時才使用。當所定義的事件是郵箱時,它指向一個消息,而當所定義的事件是消息隊列時,它指向一個數據結構(因為隊列要傳遞多個消息)。
.OSEventTbl[]和.OSEventGrp很像前面講到的OSRdyTbl[]和OSRdyGrp,只不過前兩者包含的是等待某事件的任務,而后兩者包含的是系統中處于就緒狀態的任務。
.OSEventCnt當事件是一個信號量時,.OSEventCnt是用于信號量的計數器。
(初始化時,如果信號量是用來表示一個或者多個事件的發生,那么該信號量的初始值應設為0, 如果信號量是用于對共享資源的訪問,那么該信號量的初始值應設為1)
.OSEventType定義了事件的具體類型。它可以是信號量(OS_EVENT_SEM)、郵箱(OS_EVENT_TYPE_MBOX)或消息隊列(OS_EVENT_TYPE_Q)中的一種。用戶要根據該域的具體值來調用相應的系統函數,以保證對其進行的操作的正確性。
信號量是什么?信號量有什么用?
信號量一是可以用來表示一個或多個事件的發生,二是用來對共享資源的訪問。
有時候郵箱可以當做信號量來使用,郵箱相對信號量而言,只是多傳遞了一個指針變量。其實和創建一個信號量的過程幾乎是一樣的,先申請一個空事件控制塊,接著初始化這個事件控制塊。最后返回一個指向這個事件控制塊的指針。不同之處在于事件控制塊的類型被設置成OS_EVENT_TYPE_MBOX,以及使用.OSEventPtr域來容納消息指針。
如果把郵箱比作是信號量的升級版,那消息隊列就是郵箱的升級版。郵箱可以實現從一個任務向另外一個任務發送一個指針變量,消息隊列則可以實現從一個任務向另外一個任務發送很多個指針變量。而且每個指針指向的數據結構變量也可以有所不同。(消息隊列最根本的部分是一個循環緩沖區,其中的每個單元包含一個指針。)和創建郵箱,創建信號量過程是很相似的,首先申請控制塊,接著初始化這個控制塊,和創建郵箱,信號量不同的,創建消息隊列過程是多申請了一個隊列控制塊。
最后,簡單總結一下搞懂RTOS的知識儲備:
1. 找一塊別人已經移植好代碼的MCU,把uCOS跑起來。然后去看別人的示例代碼,先搞懂怎么使用uC/OS。
嘗試著寫一個控制三個不同的led燈,它們以不同頻率閃爍。
2.等到uCOS給你提供的API用熟了, 去讀關于操作系統的書。 然后再去把堆棧、上下文、調度器、鎖、文件系統、網絡、中斷、線程、郵箱(消息隊列)等等這些概念對應到實踐中去。
試著寫一個多任務的HTTP服務器。 試著利用uCOS重構現有的代碼。
3. 去讀一個早期一點的版本uCOS的代碼, 看看uCOS是怎么實現操作系統的。
這個時候再去看操作系統原理的書。
4. 嘗試著移植uCOS到一塊新的芯片上去。 不到萬不得已不要去google。 試著獨立解決遇到的問題。
5. 改一改原始版本的uCOS代碼,看看修改了之后,操作系統的行為會如何變化。
6. 去查uCOS版本進化的歷史,看看為什么大家會如此修改這個代碼? 為什么這么設計 ?
-
嵌入式操作系統
+關注
關注
1文章
118瀏覽量
31548
原文標題:月薪20K不是夢!學會RTOS給你的身價增值
文章出處:【微信號:weixin21ic,微信公眾號:21ic電子網】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論