網站首頁 編程語言 正文
當RTOS調度器開始工作后,為了保證至少有一個任務在運行,空閑任務被自動創建,占用最低優先級(0優先級)。
xReturn = xTaskCreate( prvIdleTask, "IDLE",configMINIMAL_STACK_SIZE, (void * ) NULL, (tskIDLE_PRIORITY | portPRIVILEGE_BIT ), &xIdleTaskHandle);
空閑任務是FreeRTOS不可缺少的任務,因為FreeRTOS設計要求必須至少有一個任務處于運行狀態。我們來看一下空閑任務要做的工作。
1.釋放內存
從V9.0版本開始,如果一個任務刪除另外一個任務,被刪除任務的堆棧和TCB立即釋放。如果一個任務刪除自己,則任務的堆棧和TCB和以前一樣,通過空閑任務刪除。所以空閑任務開始就會檢查是否有任務刪除了自己,如果有的話,空閑任務負責刪除這個任務的TCB和堆??臻g。
2. 處理空閑優先級任務
當使用搶占式內核,相同優先級的任務使用時間片方式獲得CPU權限。如果有任務與空閑任務共享一個優先級,并且宏configIDLE_SHOULD_YIELD設置為1,那么空閑任務不必等到時間片耗盡再進行任務切換。
所以空閑任務檢查空閑優先級下的就緒列表中是否有多個任務,有的話則執行任務切換,讓用戶任務獲得CPU權限。
宏configIDLE_SHOULD_YIELD控制任務在空閑優先級中的行為。僅在滿足下列條件后,才會起作用。
- 使用搶占式內核調度
- 用戶任務使用空閑優先級。
通過時間片共享同一個優先級的多個任務,如果共享的優先級大于空閑優先級,并假設沒有更高優先級任務,這些任務應該獲得相同的處理器時間。
但如果共享空閑優先級時,情況會稍微有些不同。當configIDLE_SHOULD_YIELD為1時,其它共享空閑優先級的用戶任務就緒時,空閑任務立刻讓出CPU,用戶任務運行,這樣確保了能最快響應用戶任務。處于這種模式下也會有不良效果(取決于你的程序需要),描述如下:
圖中描述了四個處于空閑優先級的任務,任務A、B和C是用戶任務,任務I是空閑任務。上下文切換周期性的發生在T0、T1…T6時刻。當用戶任務運行時,空閑任務立刻讓出CPU,但是,空閑任務已經消耗了當前時間片中的一定時間。這樣的結果就是空閑任務I和用戶任務A共享一個時間片。用戶任務B和用戶任務C因此獲得了比用戶任務A更多的處理器時間。
可以通過下面方法避免:
- 如果合適的話,將處于空閑優先級的各單獨的任務放置到空閑鉤子函數中;
- 創建的用戶任務優先級大于空閑優先級;
- 設置IDLE_SHOULD_YIELD為0;
設置configIDLE_SHOULD_YIELD為0將阻止空閑任務為用戶任務讓出CPU,直到空閑任務的時間片結束。這確保所有處在空閑優先級的任務分配到相同多的處理器時間,但是,這是以分配給空閑任務更高比例的處理器時間為代價的。
3.執行空閑任務鉤子函數
空閑任務鉤子是一個函數,這個函數由用戶來實現,RTOS規定了函數的名字和參數,這個函數在每個空閑任務周期都會被調用。
要創建一個空閑鉤子:
- 設置FreeRTOSConfig.h 文件中的configUSE_IDLE_HOOK 為1;
- 定義一個函數,函數名和參數如下所示:
void vApplicationIdleHook(void );
這個鉤子函數不可以調用會引起空閑任務阻塞的API函數(例如:vTaskDelay()、帶有阻塞時間的隊列和信號量函數),在鉤子函數內部使用協程是被允許的。
使用空閑鉤子函數設置CPU進入省電模式是很常見的。
4.低功耗tickless模式
通常情況下,FreeRTOS回調空閑任務鉤子函數(需要設計者自己實現),在空閑任務鉤子函數中設置微處理器進入低功耗模式來達到省電的目的。因為系統要響應系統節拍中斷事件,因此使用這種方法會周期性的退出、再進入低功耗狀態。如果系統節拍中斷頻率過快,則大部分電能和CPU時間會消耗在進入和退出低功耗狀態上。
FreeRTOS的tickless空閑模式會在空閑周期時停止周期性系統節拍中斷。停止周期性系統節拍中斷可以使微控制器長時間處于低功耗模式。移植層需要配置外部喚醒中斷,當喚醒事件到來時,將微控制器從低功耗模式喚醒。
微控制器喚醒后,會重新使能系統節拍中斷。由于微控制器在進入低功耗后,系統節拍計數器是停止的,但我們又需要知道這段時間能折算成多少次系統節拍中斷周期,這就需要有一個不受低功耗影響的外部時鐘源,即微處理器處于低功耗模式時它也在計時的,這樣在重啟系統節拍中斷時就可以根據這個外部計時器計算出一個調整值并寫入RTOS 系統節拍計數器變量中。
空閑任務的源代碼如下所示,其中宏portTASK_FUNCTION翻譯出來為:
void prvIdleTask(void * pvParameters)。
static portTASK_FUNCTION( prvIdleTask,pvParameters ){ /*防止編譯器警告 */ (void ) pvParameters; for(;; ) { /*檢查是否有任務刪除了自己,如果有的話,空閑任務負責刪除這個任務的TCB和堆??臻g */ prvCheckTasksWaitingTermination(); #if( configUSE_PREEMPTION == 0 ) {/*如果我們沒有使用搶占式調度,我們會強制任務切換,看看是否有其它任務變得有效.如果使用搶占式調度,是不需要這樣的,因為任務變得有效后會搶占空閑任務.*/taskYIELD(); } #endif/* configUSE_PREEMPTION */ #if( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) {/* 當使用搶占式內核,相同優先級的任務使用時間片方式獲得CPU權限.如果有任務與空閑任務共享一個優先級,那么空閑任務不必等到時間片耗盡再進行任務切換.如果空閑優先級下的就緒列表中有多個任務,則執行用戶任務*/if(listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) >( UBaseType_t ) 1 ){ taskYIELD();} } #endif/* ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 )) */ #if( configUSE_IDLE_HOOK == 1 ) {externvoid vApplicationIdleHook( void );/*調用用戶定義函數.這樣允許設計者在不增加任務開銷的情況下實現后臺功能注意:這個函數中絕對不允許調用任務可能引起阻塞的函數.*/vApplicationIdleHook(); } #endif/* configUSE_IDLE_HOOK */ #if( configUSE_TICKLESS_IDLE != 0 ) { TickType_txExpectedIdleTime;/*如果每次執行空閑任務都掛起調度器,起然后再解除調度器,這很難讓人滿意,因此這里執行兩次同樣的比較(xExpectedIdleTime和configEXPECTED_IDLE_TIME_BEFORE_SLEEP),第一次比較是測試一下是否達到預期的空閑時間,并不會掛起調度器.*/xExpectedIdleTime= prvGetExpectedIdleTime();if(xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ){ vTaskSuspendAll(); { /*現在調度器被掛起,需要再次采樣空閑時間,這次空閑時間可以使用了*/ configASSERT(xNextTaskUnblockTime >= xTickCount ); xExpectedIdleTime= prvGetExpectedIdleTime(); if(xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) {portSUPPRESS_TICKS_AND_SLEEP(xExpectedIdleTime ); } } (void ) xTaskResumeAll();} } #endif/* configUSE_TICKLESS_IDLE */ }}static portTASK_FUNCTION( prvIdleTask,pvParameters ) { /*防止編譯器警告 */ (void ) pvParameters; for(;; ) { /*檢查是否有任務刪除了自己,如果有的話,空閑任務負責刪除這個任務的TCB和堆??臻g */ prvCheckTasksWaitingTermination(); #if( configUSE_PREEMPTION == 0 ) { /*如果我們沒有使用搶占式調度,我們會強制任務切換,看看是否有其它任務變得有效. 如果使用搶占式調度,是不需要這樣的,因為任務變得有效后會搶占空閑任務.*/ taskYIELD(); } #endif/* configUSE_PREEMPTION */ #if( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) { /* 當使用搶占式內核,相同優先級的任務使用時間片方式獲得CPU權限.如果有任務與空閑 任務共享一個優先級,那么空閑任務不必等到時間片耗盡再進行任務切換. 如果空閑優先級下的就緒列表中有多個任務,則執行用戶任務*/ if(listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) >( UBaseType_t ) 1 ) { taskYIELD(); } } #endif/* ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 )) */ #if( configUSE_IDLE_HOOK == 1 ) { externvoid vApplicationIdleHook( void ); /*調用用戶定義函數.這樣允許設計者在不增加任務開銷的情況下實現后臺功能 注意:這個函數中絕對不允許調用任務可能引起阻塞的函數.*/ vApplicationIdleHook(); } #endif/* configUSE_IDLE_HOOK */ #if( configUSE_TICKLESS_IDLE != 0 ) { TickType_txExpectedIdleTime; /*如果每次執行空閑任務都掛起調度器,起然后再解除調度器,這很難讓人滿意,因此這里 執行兩次同樣的比較(xExpectedIdleTime和configEXPECTED_IDLE_TIME_BEFORE_SLEEP), 第一次比較是測試一下是否達到預期的空閑時間,并不會掛起調度器.*/ xExpectedIdleTime= prvGetExpectedIdleTime(); if(xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) { vTaskSuspendAll(); { /*現在調度器被掛起,需要再次采樣空閑時間,這次空閑時間可以使用了*/ configASSERT(xNextTaskUnblockTime >= xTickCount ); xExpectedIdleTime= prvGetExpectedIdleTime(); if(xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) { portSUPPRESS_TICKS_AND_SLEEP(xExpectedIdleTime ); } } (void ) xTaskResumeAll(); } } #endif/* configUSE_TICKLESS_IDLE */ } }
原文鏈接:https://freertos.blog.csdn.net/article/details/52061032
相關推薦
- 2022-04-02 Redis快速實現分布式session的方法詳解_Redis
- 2022-12-03 .Net?Core和RabbitMQ限制循環消費的方法_實用技巧
- 2022-05-18 Golang?并發下的問題定位及解決方案_Golang
- 2022-07-31 Android?中的類文件和類加載器詳情_Android
- 2022-09-27 阿里云官方Redis開發規范總結_Redis
- 2022-06-29 Qt?Design?Studio創建工程的實現方法_C 語言
- 2022-05-12 android studio viewBinding 老爆紅
- 2022-06-26 Python使用Tkinter?GUI實現輸入驗證功能_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支