1. 程式人生 > >freertos之scheduler淺析

freertos之scheduler淺析

前言

其實是接上一篇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;
}