freertos之scheduler淺析
阿新 • • 發佈:2018-12-29
前言
其實是接上一篇task這篇文章的,上一篇寫的有點多。
程式碼分析
排程器開啟、關閉
一般在排程器沒有開啟之前需要建立一個start_task來建立一系列任務task,然後就是呼叫vTaskStartScheduler
來啟動排程器。下面分析具體程式碼
void vTaskStartScheduler( void ) { BaseType_t xReturn; /* 建立idle task,使其執行在最低優先順序 */ xReturn = xTaskCreate( prvIdleTask, "IDLE", configMINIMAL_STACK_SIZE, ( void * ) NULL, ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), &xIdleTaskHandle ); /*如果使能了定時器功能,那麼不論你之後會使用定時器功能, 都會先建立一個定時器函式*/ #if ( configUSE_TIMERS == 1 ) { if( xReturn == pdPASS ) { xReturn = xTimerCreateTimerTask(); } } if( xReturn == pdPASS ) {/*只有上兩步操作都ok的情況下,才能繼續執行*/ /*中斷在這裡被關閉,以確保在呼叫xPortStartScheduler()之前或期間不會發生tick。 已經建立的任務的棧中包含了開啟中斷的狀態字,當第一個任務執行時, 中斷將自動重新啟用開始執行。*/ portDISABLE_INTERRUPTS(); xNextTaskUnblockTime = portMAX_DELAY; xSchedulerRunning = pdTRUE; xTickCount = ( TickType_t ) 0U; /* 計時器刻度的設定是決定於硬體的,因此在移植的介面設定。*/ if( xPortStartScheduler() != pdFALSE ){////對於cm3平臺來說就是systick定時器 } } } void vTaskEndScheduler( void ) { /* 停止中斷,並呼叫可移植排程器停止函式,以便在必要時恢復原始ISRs。 可移植層必須確保中斷啟用位保持在正確的狀態。*/ portDISABLE_INTERRUPTS(); xSchedulerRunning = pdFALSE; vPortEndScheduler(); }
阻塞排程器
在系統執行的時候如果不想讓系統繼續排程task執行,可以直接阻塞排程器,這樣本來的任務就可以獨佔cpu了,不會被搶佔打斷。在做完工作之後在解掛就ok了
void vTaskSuspendAll( void ) { /* 由於該變數是BaseType_t型別的,所以不需要臨界區*/ ++uxSchedulerSuspended; } BaseType_t xTaskResumeAll( void ) { TCB_t *pxTCB = NULL; BaseType_t xAlreadyYielded = pdFALSE; /*ISR可能導致從事件中刪除任務在排程程式暫停時列出。 如果是這樣,那麼刪除的任務將被新增到xPendingReadyList。 一旦已恢復排程程式,它是安全的移動所有掛起準備任務從這個列表進入相應的就緒列表。*/ taskENTER_CRITICAL();{ --uxSchedulerSuspended; if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ){ if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U ){ /* 將 xPendingReadyList 中所有的的task移入 ReadyTasksLists中,所以用了個迴圈*/ while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE ){ pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) ); ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); prvAddTaskToReadyList( pxTCB ); /* If the moved task has a priority higher than the current task then a yield must be performed. */ if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ){ xYieldPending = pdTRUE; } } if( pxTCB != NULL ){ /*當排程器被掛起的時候,可能導致了一個任務被阻塞了,而如果這個 任務恰好使得 xNextTaskUnblockTime 沒有被重新計算, 那就在此重新計算。主要是對於低功耗無tickless實現 非常重要,這可以防止不必要的低功耗出口狀態。*/ prvResetNextTaskUnblockTime(); } /*如果在排程器暫停時發生了tick,那麼現在應該進行處理了。 這確保了tick計數不錯亂,任何延遲的任務都在正確的時間恢復。*/ { UBaseType_t uxPendedCounts = uxPendedTicks; /* Non-volatile copy. */ if( uxPendedCounts > ( UBaseType_t ) 0U ){ do{ if( xTaskIncrementTick() != pdFALSE ){ xYieldPending = pdTRUE; } --uxPendedCounts; } while( uxPendedCounts > ( UBaseType_t ) 0U ); uxPendedTicks = 0; } } if( xYieldPending != pdFALSE ){ #if( configUSE_PREEMPTION != 0 ) {//這個返回值會導致核心搶佔排程發生 xAlreadyYielded = pdTRUE; } #endif taskYIELD_IF_USING_PREEMPTION(); } } } } taskEXIT_CRITICAL(); return xAlreadyYielded; }