• 正文
    • 一、 單任務(wù)系統(tǒng)(裸機(jī))
    • 二、多任務(wù)系統(tǒng)(帶OS)
  • 相關(guān)推薦
申請入駐 產(chǎn)業(yè)圖譜

FreeRTOS學(xué)習(xí)筆記 | 基礎(chǔ)知識(shí)體系梳理

2022/09/19
1230
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

我發(fā)現(xiàn)學(xué)習(xí) RTOS 是學(xué)習(xí) Linux 內(nèi)核的好方法。大有彎道超車的可能。

1. 任務(wù)堆棧

2. 任務(wù)狀態(tài)

3. 任務(wù)優(yōu)先級

4. 任務(wù)調(diào)度

5. 臨界區(qū)、鎖與系統(tǒng)時(shí)間

  • 1.1 任務(wù)棧大小確定

    1.2 棧溢出檢測機(jī)制

    3.1任務(wù)優(yōu)先級說明

    3.2 任務(wù)優(yōu)先級分配方案

    3.3 任務(wù)優(yōu)先級與終端優(yōu)先級的區(qū)別

    4.1 調(diào)度器

    5.1 臨界區(qū)與開關(guān)中斷

    5.2 鎖

    5.3 FreeRTOS 系統(tǒng)時(shí)鐘節(jié)拍和時(shí)間管理

一、 單任務(wù)系統(tǒng)(裸機(jī))

主要是采用超級循環(huán)系統(tǒng)(前后臺(tái)系統(tǒng)),應(yīng)用程序是一個(gè)無限的循環(huán),循環(huán)中調(diào)用相應(yīng)的函數(shù)完成相應(yīng)的操作,這部分可以看做后臺(tái)行為;中斷服務(wù)程序處理異步事件,這部分可以看做是前臺(tái)行為。后臺(tái)也可以叫做任務(wù)級,前臺(tái)也叫作中斷級。

前后臺(tái)系統(tǒng)的編程思路有兩種:輪詢方式(實(shí)時(shí)性得不到保障,緊急與非緊急消息不能有效管理)、中斷方式(可以保證一定的實(shí)時(shí)性,緊急消息可以得到響應(yīng))。

 

采用中斷和查詢結(jié)合的方式可以解決大部分裸機(jī)應(yīng)用,但隨著工程的復(fù)雜,裸機(jī)方式的缺點(diǎn)就暴露出來了:

必須在中斷(ISR)內(nèi)處理時(shí)間關(guān)鍵運(yùn)算:

ISR 函數(shù)變得非常復(fù)雜,并且需要很長執(zhí)行時(shí)間。

ISR 嵌套可能產(chǎn)生不可預(yù)測的執(zhí)行時(shí)間和堆棧需求。

超級循環(huán)和 ISR 之間的數(shù)據(jù)交換是通過全局共享變量進(jìn)行的:應(yīng)用程序的程序員必須確保數(shù)據(jù)一致性。

超級循環(huán)可以與系統(tǒng)計(jì)時(shí)器輕松同步,但:

如果系統(tǒng)需要多種不同的周期時(shí)間,則會(huì)很難實(shí)現(xiàn)。

超過超級循環(huán)周期的耗時(shí)函數(shù)需要做拆分。

增加軟件開銷,應(yīng)用程序難以理解。

超級循環(huán)使得應(yīng)用程序變得非常復(fù)雜,因此難以擴(kuò)展:一個(gè)簡單的更改就可能產(chǎn)生不可預(yù)測的副作用,對這種副作用進(jìn)行分析非常耗時(shí)。超級循環(huán)概念的這些缺點(diǎn)可以通過使用實(shí)時(shí)操作系統(tǒng) (RTOS) 來解決。

二、多任務(wù)系統(tǒng)(帶OS)

采用多任務(wù)系統(tǒng)可以以上的裸機(jī)開發(fā)遇到的4大缺點(diǎn)。

 

RTOS的實(shí)現(xiàn)重點(diǎn)就在這個(gè)OS任務(wù)調(diào)度器上,調(diào)度器的作用就是使用相關(guān)的調(diào)度算法來決定當(dāng)前需要執(zhí)行的任務(wù)。

FreeRTOS就是一款支持多任務(wù)運(yùn)行的實(shí)時(shí)操作系統(tǒng),具有時(shí)間片、搶占式和合作式三種調(diào)度方式。通過 FreeRTOS 實(shí)時(shí)操作系統(tǒng)可以將程序函數(shù)分成獨(dú)立的任務(wù),并為其提供合理的調(diào)度方式。

1. 任務(wù)堆棧

 

棧大小 0x400 = 1024,單位字節(jié)。在RTOS下,上面截圖里設(shè)置的棧大小有了一個(gè)新名字叫做系統(tǒng)??臻g,而任務(wù)棧是不使用這里的空間,哪里使用這里的棧空間呢,實(shí)際上是中斷函數(shù)和中斷嵌套。

由于 Cortex-M3 和 M4 內(nèi)核具有雙堆棧指針,MSP 主堆棧指針和 PSP 進(jìn)程堆棧指針,或者叫 PSP 任務(wù)堆棧指針也是可以的。在 FreeRTOS 操作系統(tǒng)中,主堆棧指針 MSP 是給系統(tǒng)??臻g使用的,進(jìn) 程堆棧指針 PSP 是給任務(wù)棧使用的。也就是說,在 FreeRTOS 任務(wù)中,所有棧空間的使用都是通過 PSP 指針進(jìn)行指向的。一旦進(jìn)入了中斷函數(shù)以及可能發(fā)生的中斷嵌套都是用的 MSP 指針。這個(gè)知識(shí) 點(diǎn)要記住它,當(dāng)前可以不知道這是為什么,但是一定要記住。

實(shí)際應(yīng)用中系統(tǒng)棧空間分配多大,主要是看可能發(fā)生的中斷嵌套層數(shù),下面我們就按照最壞執(zhí)行情況 進(jìn)行考慮,所有的寄存器都需要入棧,此時(shí)分為兩種情況:

64 字節(jié):對于 Cortex-M3 內(nèi)核和未使用 FPU(浮點(diǎn)運(yùn)算單元)功能的 Cortex-M4 內(nèi)核在發(fā)生中斷時(shí)需要將 16 個(gè)通用寄存器全部入棧,每個(gè)寄存器占用 4 個(gè)字節(jié),也就是 16*4 = 64 字節(jié)的空間??赡馨l(fā)生幾次中斷嵌套就是要 64 乘以幾即可。當(dāng)然,這種是最壞執(zhí)行情況,也就是所有的寄存器都入棧。(注:任務(wù)執(zhí)行的過程中發(fā)生中斷的話,有 8 個(gè)寄存器是自動(dòng)入棧的,這個(gè)棧是任務(wù)棧,進(jìn)入中斷以后其余寄存器入棧以及發(fā)生中斷嵌套都是用的系統(tǒng)棧)

200 字節(jié): 對于具有 FPU(浮點(diǎn)運(yùn)算單元)功能的 Cortex-M4 內(nèi)核,如果在任務(wù)中進(jìn)行了浮點(diǎn)運(yùn)算,那么在發(fā)生中斷的時(shí)候除了 16 個(gè)通用寄存器需要入棧,還有 34 個(gè)浮點(diǎn)寄存器也是要入棧的,也就是(16+34)*4 = 200 字節(jié)的空間。當(dāng)然,這種是最壞執(zhí)行情況,也就是所有的寄存器都入棧。

1.1 任務(wù)棧大小確定

函數(shù)的棧大小計(jì)算起來是比較麻煩的,那么有沒有簡單的辦法來計(jì)算呢?有的,一般 IDE 開發(fā)環(huán)境都有這樣的功能,比如 MDK 會(huì)生成一個(gè) htm 文件,通過這個(gè)文件用戶可以知道每個(gè)被調(diào)用函數(shù)的最大棧需求以及各個(gè)函數(shù)之間的調(diào)用關(guān)系。但是 MDK 無法確定通過函數(shù)指針實(shí)現(xiàn)函數(shù)調(diào)用時(shí)的棧需求。另外,發(fā)生中斷或中斷嵌套時(shí)的現(xiàn)場保護(hù)需要的??臻g也不會(huì)統(tǒng)計(jì)。

一般來說,用戶可以事先給任務(wù)分配一個(gè)大的??臻g,然后通過打印任務(wù)棧的使用情況,運(yùn)行一段時(shí)間就會(huì)有個(gè)大概的范圍了。這種方法比較簡單且實(shí)用些。

1.2 棧溢出檢測機(jī)制

棧生長方向從高地址向低地址生長(M4 和 M3 是這種方式)

 

上圖標(biāo)識(shí) 3 的位置是局部變量 int i 和 int array[10]占用的??臻g,但申請了??臻g后已經(jīng)越界了。這個(gè)就是所謂的棧溢出了。如果用戶在函數(shù) test 中通過數(shù)組 array 修改了這部分越界區(qū)的數(shù)據(jù)且這部分越界的??臻g暫時(shí)沒有用到或者數(shù)據(jù)不是很重要,情況還不算嚴(yán)重,但是如果存儲(chǔ)的是關(guān)鍵數(shù)據(jù),會(huì)直接導(dǎo)致系統(tǒng)崩潰。

上圖標(biāo)識(shí) 4 的位置是局部變量申請了??臻g后,棧指針向下偏移(返回地址+變量 i+10 個(gè)數(shù)組元素)*4 =48 個(gè)字節(jié)。

上圖標(biāo)識(shí) 5 的位置可能是其它任務(wù)的棧空間,也可能是全局變量或者其它用途的存儲(chǔ)區(qū),如果 test函數(shù)在使用中還有用到棧的地方就會(huì)從這里申請,這部分越界的空間暫時(shí)沒有用到或者數(shù)據(jù)不是很重要,情況還不算嚴(yán)重,但是如果存儲(chǔ)的是關(guān)鍵數(shù)據(jù),會(huì)直接導(dǎo)致系統(tǒng)崩潰。

FreeRTOS 提供了兩種棧溢出檢測機(jī)制,這兩種檢測都是在任務(wù)切換時(shí)才會(huì)進(jìn)行:

在任務(wù)切換時(shí)檢測任務(wù)棧指針是否過界了,如果過界了,在任務(wù)切換的時(shí)候會(huì)觸發(fā)棧溢出鉤子函數(shù)。void vApplicationStackOverflowHook( TaskHandle_t xTask,signed char *pcTaskName ); 用戶可以在鉤子函數(shù)里面做一些處理。這種方法不能保證所有的棧溢出都能檢測到。比如任務(wù)在執(zhí)行的過程中出現(xiàn)過棧溢出。任務(wù)切換前棧指針又恢復(fù)到了正常水平,這種情況在任務(wù)切換的時(shí)候是檢測不到的。又比如任務(wù)棧溢出后,把這部分棧區(qū)的數(shù)據(jù)修改了,這部分棧區(qū)的數(shù)據(jù)不重要或者暫時(shí)沒有用到還好,但如果是重要數(shù)據(jù)被修改將直接導(dǎo)致系統(tǒng)進(jìn)入硬件異常,這種情況下,棧溢出檢測功能也是檢測不到的。

使用方法一需要用戶在 FreeRTOSConfig.h 文件中配置如下宏定義:

#define configCHECK_FOR_STACK_OVERFLOW 1

任務(wù)創(chuàng)建的時(shí)候?qū)⑷蝿?wù)棧所有數(shù)據(jù)初始化為 0xa5,任務(wù)切換時(shí)進(jìn)行任務(wù)棧檢測的時(shí)候會(huì)檢測末尾的 16 個(gè)字節(jié)是否都是 0xa5,通過這種方式來檢測任務(wù)棧是否溢出了。相比方法一,這種方法的速度稍慢些,但是這樣就有效地避免了方法一里面的部分情況。不過依然不能保證所有的棧溢出都能檢測到,比如任務(wù)棧末尾的 16 個(gè)字節(jié)沒有用到,即沒有被修改,但是任務(wù)棧已經(jīng)溢出了,這種情況是檢測不到的。另外任務(wù)棧溢出后,任務(wù)棧末尾的 16 個(gè)字節(jié)沒有修改,但是溢出部分的棧區(qū)數(shù)據(jù)被修改了,這部分棧區(qū)的數(shù)據(jù)不重要或者暫時(shí)沒有用到還好,但如果是重要數(shù)據(jù)被修改將直接導(dǎo)致系統(tǒng)進(jìn)入硬件異常,這種情況下,棧溢出檢測功能也是檢測不到的。

使用方法二需要用戶在 FreeRTOSConfig.h 文件中配置如下宏定義:

#define configCHECK_FOR_STACK_OVERFLOW 2

除了 FreeRTOS 提供的這兩種棧溢出檢測機(jī)制,還有其它的棧溢出檢測機(jī)制,大家可以在 Mircrium 官方發(fā)布的如下這個(gè)博文中學(xué)習(xí):https://www.micrium.com/detecting-stack-overflows-part-2-of-2/

2. 任務(wù)狀態(tài)

FreeRTOS的任務(wù)狀態(tài)(4種):1.運(yùn)行態(tài)(Running) 2.就緒態(tài)(Ready) 3.阻塞態(tài)(Blocked) 4.掛起態(tài)(Suspended)

ucos的任務(wù)狀態(tài)(5種):1.睡眠狀態(tài) 2.就緒狀態(tài) 3.等待狀態(tài) 4.中斷服務(wù)狀態(tài) 5.執(zhí)行狀態(tài)

Running—運(yùn)行態(tài)

當(dāng)任務(wù)處于實(shí)際運(yùn)行狀態(tài)被稱之為運(yùn)行態(tài),即 CPU 的使用權(quán)被這個(gè)任務(wù)占用。

Ready—就緒態(tài)

處于就緒態(tài)的任務(wù)是指那些能夠運(yùn)行(沒有被阻塞和掛起),但是當(dāng)前沒有運(yùn)行的任務(wù),因?yàn)橥瑑?yōu)先 級或更高優(yōu)先級的任務(wù)正在運(yùn)行。

Blocked—阻塞態(tài)

由于等待信號(hào)量,消息隊(duì)列,事件標(biāo)志組等而處于的狀態(tài)被稱之為阻塞態(tài),另外任務(wù)調(diào)用延遲函數(shù)也 會(huì)處于阻塞態(tài)。

Suspended—掛起態(tài)

類似阻塞態(tài),通過調(diào)用函數(shù) vTaskSuspend()對指定任務(wù)進(jìn)行掛起,掛起后這個(gè)任務(wù)將不被執(zhí)行,只 有調(diào)用函數(shù) xTaskResume()才可以將這個(gè)任務(wù)從掛起態(tài)恢復(fù)。

 

3. 任務(wù)優(yōu)先級

3.1任務(wù)優(yōu)先級說明

FreeRTOS 中任務(wù)的最高優(yōu)先級是通過 FreeRTOSConfig.h 文件中的 configMAX_PRIORITIES 進(jìn)行配置的,用戶實(shí)際可以使用的優(yōu)先級范圍是 0 到 configMAX_PRIORITIES – 1。比如我們配置此宏定義為 5,那么用戶可以使用的優(yōu)先級號(hào)是 0,1,2,3,4,不包含 5,對于這一點(diǎn),初學(xué)者要特別的注意。

用戶配置任務(wù)的優(yōu)先級數(shù)值越小,那么此任務(wù)的優(yōu)先級越低,空閑任務(wù)的優(yōu)先級是 0。

建議用戶配置宏定義 configMAX_PRIORITIES 的最大值不要超過 32,即用戶任務(wù)可以使用的優(yōu)先級范圍是0到31。

3.2 任務(wù)優(yōu)先級分配方案

 

IRQ 任務(wù):IRQ 任務(wù)是指通過中斷服務(wù)程序進(jìn)行觸發(fā)的任務(wù),此類任務(wù)應(yīng)該設(shè)置為所有任務(wù)里面優(yōu)先級最高的。

高優(yōu)先級后臺(tái)任務(wù):比如按鍵檢測,觸摸檢測,USB 消息處理,串口消息處理等,都可以歸為這一類任務(wù)。

低優(yōu)先級的時(shí)間片調(diào)度任務(wù):比如 emWin 的界面顯示,LED 數(shù)碼管的顯示等不需要實(shí)時(shí)執(zhí)行的都可以歸為這一類任務(wù)。實(shí)際應(yīng)用中用戶不必拘泥于將這些任務(wù)都設(shè)置為優(yōu)先級 1 的同優(yōu)先級任務(wù),可以設(shè)置多個(gè)優(yōu)先級,只需注意這類任務(wù)不需要高實(shí)時(shí)性。

空閑任務(wù):空閑任務(wù)是系統(tǒng)任務(wù)。

特別注意:IRQ 任務(wù)和高優(yōu)先級任務(wù)必須設(shè)置為阻塞式(調(diào)用消息等待或者延遲等函數(shù)即可),只有這樣,高優(yōu)先級任務(wù)才會(huì)釋放 CPU 的使用權(quán),,從而低優(yōu)先級任務(wù)才有機(jī)會(huì)得到執(zhí)行。這里的優(yōu)先級分配方案是我們推薦的一種方式,實(shí)際項(xiàng)目也可以不采用這種方法。調(diào)試出適合項(xiàng)目需求的才是最好的。

3.3 任務(wù)優(yōu)先級與終端優(yōu)先級的區(qū)別

這兩個(gè)之間沒有任何關(guān)系,不管中斷的優(yōu)先級是多少,中斷的優(yōu)先級永遠(yuǎn)高于任何任務(wù)的優(yōu)先級,即任務(wù)在執(zhí)行的過程中,中斷來了就開始執(zhí)行中斷服務(wù)程序。

另外對于 STM32F103,F(xiàn)407 和 F429 來說,中斷優(yōu)先級的數(shù)值越小,優(yōu)先級越高。而 FreeRTOS的任務(wù)優(yōu)先級是,任務(wù)優(yōu)先級數(shù)值越小,任務(wù)優(yōu)先級越低。

4. 任務(wù)調(diào)度

FreeRTOS就是一款支持多任務(wù)運(yùn)行的實(shí)時(shí)操作系統(tǒng),具有時(shí)間片、搶占式和合作式三種調(diào)度方式。

合作式調(diào)度,主要用在資源有限的設(shè)備上面,現(xiàn)在已經(jīng)很少使用了。出于這個(gè)原因,后面的 FreeRTOS 版本中不會(huì)將合作式調(diào)度刪除掉,但也不會(huì)再進(jìn)行升級了。

搶占式調(diào)度,每個(gè)任務(wù)都有不同的優(yōu)先級,任務(wù)會(huì)一直運(yùn)行直到被高優(yōu)先級任務(wù)搶占或者遇到阻塞式的 API 函數(shù),比如 vTaskDelay。

時(shí)間片調(diào)度,每個(gè)任務(wù)都有相同的優(yōu)先級,任務(wù)會(huì)運(yùn)行固定的時(shí)間片個(gè)數(shù)或者遇到阻塞式的 API 函數(shù),比如vTaskDelay,才會(huì)執(zhí)行同優(yōu)先級任務(wù)之間的任務(wù)切換。

4.1 調(diào)度器

調(diào)度器就是使用相關(guān)的調(diào)度算法來決定當(dāng)前需要執(zhí)行的任務(wù)。所有的調(diào)度器有一個(gè)共同的 特性:

調(diào)度器可以區(qū)分就緒態(tài)任務(wù)和掛起任務(wù)(由于延遲,信號(hào)量等待,郵箱等待,事件組等待等原因而使 得任務(wù)被掛起)。
調(diào)度器可以選擇就緒態(tài)中的一個(gè)任務(wù),然后激活它(通過執(zhí)行這個(gè)任務(wù))。當(dāng)前正在執(zhí)行的任務(wù)是運(yùn) 行態(tài)的任務(wù)。

不同調(diào)度器之間最大的區(qū)別就是如何分配就緒態(tài)任務(wù)間的完成時(shí)間。

嵌入式實(shí)時(shí)操作系統(tǒng)的核心就是調(diào)度器和任務(wù)切換,調(diào)度器的核心就是調(diào)度算法。任務(wù)切換的實(shí)現(xiàn)在不同的嵌 入式實(shí)時(shí)操作系統(tǒng)中區(qū)別不大,基本相同的硬件內(nèi)核架構(gòu),任務(wù)切換也是相似的。調(diào)度算法就有些區(qū)別了。

4.1.1 搶占式調(diào)度器

使用了搶占式調(diào)度,最高優(yōu)先級的任務(wù)一旦就緒,總能得到 CPU 的控制權(quán)。比如,當(dāng)一個(gè)運(yùn)行著的任務(wù)被其它高優(yōu)先級的任務(wù)搶占,當(dāng)前任務(wù)的 CPU 使用權(quán)就被剝奪了,或者說被掛起了,那個(gè)高優(yōu)先級的任務(wù)立刻得到了 CPU 的控制權(quán)并運(yùn)行。又比如,如果中斷服務(wù)程序使一個(gè)高優(yōu)先級的任務(wù)進(jìn)入就緒態(tài),中斷完成時(shí),被中斷的低優(yōu)先級任務(wù)被掛起,優(yōu)先級高的那個(gè)任務(wù)開始運(yùn)行。使用搶占式調(diào)度器,使得最高優(yōu)先級的任務(wù)什么時(shí)候可以得到 CPU 的控制權(quán)并運(yùn)行是可知的,同時(shí)使得任務(wù)級響應(yīng)時(shí)間得以最優(yōu)化。

總的來說,學(xué)習(xí)搶占式調(diào)度要掌握的最關(guān)鍵一點(diǎn)是:每個(gè)任務(wù)都被分配了不同的優(yōu)先級,搶占式調(diào)度器會(huì)獲得就緒列表中優(yōu)先級最高的任務(wù),并運(yùn)行這個(gè)任務(wù)。

在 FreeRTOS 的配置文件 FreeRTOSConfig.h 中禁止使用時(shí)間片調(diào)度,那么每個(gè)任務(wù)必須配置不同的優(yōu)先級。當(dāng) FreeRTOS 多任務(wù)啟動(dòng)執(zhí)行后,基本會(huì)按照如下的方式去執(zhí)行:

首先執(zhí)行的最高優(yōu)先級的任務(wù) Task1,Task1 會(huì)一直運(yùn)行直到遇到系統(tǒng)阻塞式的 API 函數(shù),比如延遲,事件標(biāo)志等待,信號(hào)量等待,Task1 任務(wù)會(huì)被掛起,也就是釋放 CPU 的執(zhí)行權(quán),讓低優(yōu)先級的任務(wù)得到執(zhí)行。

FreeRTOS 操作系統(tǒng)繼續(xù)執(zhí)行任務(wù)就緒列表中下一個(gè)最高優(yōu)先級的任務(wù) Task2,Task2 執(zhí)行過程中有兩種情況:

Task1由于延遲時(shí)間到,接收到信號(hào)量消息等方面的原因,使得 Task1從掛起狀態(tài)恢復(fù)到就緒態(tài), 在搶占式調(diào)度器的作用下,Task2 的執(zhí)行會(huì)被 Task1 搶占。

Task2 會(huì)一直運(yùn)行直到遇到系統(tǒng)阻塞式的 API 函數(shù),比如延遲,事件標(biāo)志等待,信號(hào)量等待,Task2任務(wù)會(huì)被掛起,繼而執(zhí)行就緒列表中下一個(gè)最高優(yōu)先級的任務(wù)。

如果用戶創(chuàng)建了多個(gè)任務(wù)并且采用搶占式調(diào)度器的話,基本都是按照上面兩條來執(zhí)行。根據(jù)搶占式調(diào)度器,當(dāng)前的任務(wù)要么被高優(yōu)先級任務(wù)搶占,要么通過調(diào)用阻塞式 API 來釋放 CPU 使用權(quán)讓低優(yōu)先級任務(wù)執(zhí)行,沒有用戶任務(wù)執(zhí)行時(shí)就執(zhí)行空閑任務(wù).

 

4.1.2 時(shí)間片調(diào)度器

在小型的嵌入式 RTOS 中,最常用的的時(shí)間片調(diào)度算法就是 Round-robin 調(diào)度算法。這種調(diào)度算法可以用于搶占式或者合作式的多任務(wù)中。另外,時(shí)間片調(diào)度適合用于不要求任務(wù)實(shí)時(shí)響應(yīng)的情況。

實(shí)現(xiàn) Round-robin 調(diào)度算法需要給同優(yōu)先級的任務(wù)分配一個(gè)專門的列表,用于記錄當(dāng)前就緒的任務(wù),并為每個(gè)任務(wù)分配一個(gè)時(shí)間片(也就是需要運(yùn)行的時(shí)間長度,時(shí)間片用完了就進(jìn)行任務(wù)切換)。

在 FreeRTOS 操作系統(tǒng)中只有同優(yōu)先級任務(wù)才會(huì)使用時(shí)間片調(diào)度,另外還需要用戶在FreeRTOSConfig.h 文件中使能宏定義:

#define configUSE_TIME_SLICING 1

默認(rèn)情況下,此宏定義已經(jīng)在 FreeRTOS.h 文件里面使能了,用戶可以不用在FreeRTOSConfig.h 文件中再單獨(dú)使能。示例:

創(chuàng)建 4 個(gè)同優(yōu)先級任務(wù) Task1,Task2,Task3 和 Task4。

先運(yùn)行任務(wù) Task1,運(yùn)行夠 5 個(gè)系統(tǒng)時(shí)鐘節(jié)拍后,通過時(shí)間片調(diào)度切換到任務(wù) Task2。

任務(wù) Task2 運(yùn)行夠 5 個(gè)系統(tǒng)時(shí)鐘節(jié)拍后,通過時(shí)間片調(diào)度切換到任務(wù) Task3。

任務(wù) Task3 在運(yùn)行期間調(diào)用了阻塞式 API 函數(shù),調(diào)用函數(shù)時(shí),雖然 5 個(gè)系統(tǒng)時(shí)鐘節(jié)拍的時(shí)間片大小還沒有用完,此時(shí)依然會(huì)通過時(shí)間片調(diào)度切換到下一個(gè)任務(wù) Task4。(注意,沒有用完的時(shí)間片不會(huì)再使用,下次任務(wù) Task3 得到執(zhí)行還是按照 5 個(gè)系統(tǒng)時(shí)鐘節(jié)拍運(yùn)行)

任務(wù) Task4 運(yùn)行夠 5 個(gè)系統(tǒng)時(shí)鐘節(jié)拍后,通過時(shí)間片調(diào)度切換到任務(wù) Task1。

 

 

5. 臨界區(qū)、鎖與系統(tǒng)時(shí)間

5.1 臨界區(qū)與開關(guān)中斷

代碼的臨界臨界區(qū),一旦這部分代碼開始執(zhí)行,則不允許任何中斷打斷。為確保臨界區(qū)代碼的執(zhí)行不被中斷,在進(jìn)入臨界區(qū)之前須關(guān)中斷,而臨界區(qū)代碼執(zhí)行完畢后,要立即開中斷。

FreeRTOS 的源碼中有多處臨界段的地方, 臨界段雖然保護(hù)了關(guān)鍵代碼的執(zhí)行不被打斷, 但也會(huì)影響系統(tǒng)的實(shí)時(shí)性。比如此時(shí)某個(gè)任務(wù)正在調(diào)用系統(tǒng) API 函數(shù),而且此時(shí)中斷正好關(guān)閉了,也就是進(jìn)入到了臨界區(qū)中,這個(gè)時(shí)候如果有一個(gè)緊急的中斷事件被觸發(fā),這個(gè)中斷就不能得到及時(shí)執(zhí)行,必須等到中斷開啟才可以得到執(zhí)行, 如果關(guān)中斷時(shí)間超過了緊急中斷能夠容忍的限度, 危害是可想而知的。

FreeRTOS 源碼中就有多處臨界段的處理,跟 FreeRTOS 一樣,uCOS-II 和 uCOS-III 源碼中都是有臨界段的,而 RTX 的源碼中不存在臨界段。另外,除了 FreeRTOS 操作系統(tǒng)源碼所帶的臨界段以外,用戶寫應(yīng)用的時(shí)候也有臨界段的問題,比如以下兩種:

讀取或者修改變量(特別是用于任務(wù)間通信的全局變量)的代碼,一般來說這是最常見的臨界代碼。

調(diào)用公共函數(shù)的代碼,特別是不可重入的函數(shù),如果多個(gè)任務(wù)都訪問這個(gè)函數(shù),結(jié)果是可想而知的??傊瑢τ谂R界段要做到執(zhí)行時(shí)間越短越好,否則會(huì)影響系統(tǒng)的實(shí)時(shí)性。

重入函數(shù)的實(shí)現(xiàn)特征一般而言,重入函數(shù)具有如下特征:

函數(shù)內(nèi)部無整個(gè)軟件生命周期的變量(靜態(tài)變量)
未引用或者訪問整個(gè)軟件生命周期的變量(全局變量)

任務(wù)代碼臨界區(qū)處理

FreeRTOS 任務(wù)代碼中臨界段的進(jìn)入和退出主要是通過操作寄存器 basepri 實(shí)現(xiàn)的。進(jìn)入臨界段前操作寄存器 basepri 關(guān)閉了所有小于等于宏定義configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY所定義的中斷優(yōu)先級,這樣臨界段代碼就不會(huì)被中斷干擾到,而且實(shí)現(xiàn)任務(wù)切換功能的 PendSV 中斷和滴答定時(shí)器中斷是最低優(yōu)先級中斷,所以此任務(wù)在執(zhí)行臨界段代碼期間是不會(huì)被其它高優(yōu)先級任務(wù)打斷的。退出臨界段時(shí)重新操作 basepri 寄存器,即打開被關(guān)閉的中斷(這里我們不考慮不受 FreeRTOS 管理的更高優(yōu)先級中斷)。

中斷服務(wù)程序的臨界區(qū)處理

與任務(wù)代碼里臨界段的處理方式類似,中斷服務(wù)程序里面臨界段的處理也有一對開關(guān)中斷函數(shù)。

5.2 鎖

調(diào)度鎖

調(diào)度鎖就是 RTOS 提供的調(diào)度器開關(guān)函數(shù),如果某個(gè)任務(wù)調(diào)用了調(diào)度鎖開關(guān)函數(shù),處于調(diào)度鎖開和調(diào)度鎖關(guān)之間的代碼在執(zhí)行期間是不會(huì)被高優(yōu)先級的任務(wù)搶占的,即任務(wù)調(diào)度被禁止。這一點(diǎn)要跟臨界段的作用區(qū)分開,調(diào)度鎖只是禁止了任務(wù)調(diào)度,并沒有關(guān)閉任何中斷,中斷還是正常執(zhí)行的。而臨界段進(jìn)行了開關(guān)中斷操作。調(diào)度鎖相關(guān)函數(shù);

任務(wù)鎖

簡單的說,為了防止當(dāng)前任務(wù)的執(zhí)行被其它高優(yōu)先級的任務(wù)打斷而提供的鎖機(jī)制就是任務(wù)鎖。

FreeRTOS 也沒有專門的任務(wù)鎖函數(shù),但是使用 FreeRTOS 現(xiàn)有的功能有兩種實(shí)現(xiàn)方法:

(1) 通過給調(diào)度器加鎖實(shí)現(xiàn)。利用 FreeRTOS 的調(diào)度鎖功能給調(diào)度器加鎖的話,將關(guān)閉任務(wù)切換功能,從而高優(yōu)先級任務(wù)也就無法搶占低優(yōu)先級任務(wù)的執(zhí)行,同時(shí)高優(yōu)先級任務(wù)也是無法向低優(yōu)先級任務(wù)切換的。另外特別注意,調(diào)度鎖只是禁止了調(diào)度器工作,并沒有關(guān)閉任何中斷。

(2) 通過關(guān)閉任務(wù)切換中斷 PendSV 和系統(tǒng)時(shí)鐘節(jié)拍中斷 Systick利用 FreeRTOS 的任務(wù)代碼臨界段處理函數(shù)就可以關(guān)閉 PendSV 中斷和 Systick 中斷。因?yàn)檫M(jìn)入臨界段前,操作寄存器 basepri 關(guān)閉了所有小于等于宏定義configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 所定義的中斷優(yōu)先級(實(shí)現(xiàn)任務(wù)切換功能的 PendSV 中斷和滴答定時(shí)器中斷是最低優(yōu)先級中斷,所以也是被關(guān)閉的),這樣低優(yōu)先級任務(wù)在執(zhí)行臨界段代碼期間是不會(huì)被高優(yōu)先級任務(wù)打斷的,從而就實(shí)現(xiàn)了任務(wù)鎖的效果。

中斷鎖

中斷鎖就是 RTOS 提供的開關(guān)中斷函數(shù),F(xiàn)reeRTOS 沒有專門的中斷鎖函數(shù),使用中斷服務(wù)程序臨界段處理函數(shù)就可以實(shí)現(xiàn)同樣效果。

5.3 FreeRTOS 系統(tǒng)時(shí)鐘節(jié)拍和時(shí)間管理

5.3.1 FreeRTOS 時(shí)鐘節(jié)拍

任何操作系統(tǒng)都需要提供一個(gè)時(shí)鐘節(jié)拍,以供系統(tǒng)處理諸如延時(shí)、 超時(shí)等與時(shí)間相關(guān)的事件。時(shí)鐘節(jié)拍是特定的周期性中斷,這個(gè)中斷可以看做是系統(tǒng)心跳。中斷之間的時(shí)間間隔取決于不同的應(yīng)用,一般是 1ms – 100ms。時(shí)鐘的節(jié)拍中斷使得內(nèi)核可以將任務(wù)延遲若干個(gè)時(shí)鐘節(jié)拍,以及當(dāng)任務(wù)等待事件發(fā)生時(shí),提供等待超時(shí)等依據(jù)。時(shí)鐘節(jié)拍率越快,系統(tǒng)的額外開銷就越大。任何操作系統(tǒng)都需要提供一個(gè)時(shí)鐘節(jié)拍,以供系統(tǒng)處理諸如延時(shí)、 超時(shí)等與時(shí)間相關(guān)的事件。時(shí)鐘節(jié)拍是特定的周期性中斷,這個(gè)中斷可以看做是系統(tǒng)心跳。中斷之間的時(shí)間間隔取決于不同的應(yīng)用,一般是 1ms – 100ms。時(shí)鐘的節(jié)拍中斷使得內(nèi)核可以將任務(wù)延遲若干個(gè)時(shí)鐘節(jié)拍,以及當(dāng)任務(wù)等待事件發(fā)生時(shí),提供等待超時(shí)等依據(jù)。時(shí)鐘節(jié)拍率越快,系統(tǒng)的額外開銷就越大。

對于 Cortex-M3 內(nèi)核的 STM32F103 和 Cortex-M4 內(nèi)核的 STM32F407 以及 F429,教程配套的例子都是用滴答定時(shí)器來實(shí)現(xiàn)系統(tǒng)時(shí)鐘節(jié)拍的。

5.3.2 FreeRTOS 時(shí)間管理

時(shí)間管理功能是 FreeRTOS 操作系統(tǒng)里面最基本的功能,同時(shí)也是必須要掌握好的。

時(shí)間延時(shí)

FreeRTOS 中的時(shí)間延遲函數(shù)主要有以下兩個(gè)作用:

為周期性執(zhí)行的任務(wù)提供延遲。

對于搶占式調(diào)度器,讓高優(yōu)先級任務(wù)可以通過時(shí)間延遲函數(shù)釋放 CPU 使用權(quán),從而讓低優(yōu)先級任務(wù)可以得到執(zhí)行。

通過如下的框圖來說明一下延遲函數(shù)對任務(wù)運(yùn)行狀態(tài)的影響,有一個(gè)形象的認(rèn)識(shí)。

對任務(wù) Task1 的運(yùn)行狀態(tài)做說明,調(diào)度器支持時(shí)間片調(diào)度和搶占式調(diào)度。運(yùn)行過程描述如下:

起初任務(wù) Task1 處于運(yùn)行態(tài),調(diào)用 vTaskDelay 函數(shù)后進(jìn)入到阻塞狀態(tài),也就是 blocked 狀態(tài)。

vTaskDelay 函數(shù)設(shè)置的延遲時(shí)間到,由于任務(wù) Task1 不是當(dāng)前就緒的最高優(yōu)先級任務(wù),所以不能進(jìn) 入到運(yùn)行狀態(tài),只能進(jìn)入到就緒狀態(tài),也就是 ready 狀態(tài)。

一段時(shí)間后,調(diào)度器發(fā)現(xiàn)任務(wù) Task1 是當(dāng)前就緒的最高優(yōu)先級任務(wù),從而任務(wù)從就緒態(tài)切換到運(yùn)行態(tài)。

由于時(shí)間片調(diào)度,任務(wù) Task1 由運(yùn)行態(tài)切換到就緒態(tài)。

FreeRTOS

FreeRTOS

FreeRTOS 專職開發(fā)人員一直與芯片公司緊密合作, 為客戶提供市場領(lǐng)先external_link以及免費(fèi)的商用級、高品質(zhì) RTOS和工具。

FreeRTOS 專職開發(fā)人員一直與芯片公司緊密合作, 為客戶提供市場領(lǐng)先external_link以及免費(fèi)的商用級、高品質(zhì) RTOS和工具。收起

查看更多

相關(guān)推薦

登錄即可解鎖
  • 海量技術(shù)文章
  • 設(shè)計(jì)資源下載
  • 產(chǎn)業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄

針對嵌入式人工智能,物聯(lián)網(wǎng)等專業(yè)技術(shù)分享和交流平臺(tái),內(nèi)容涉及arm,linux,android等各方面。