FreeRTOS之vTaskStartScheduler实现分析

FreeRTOS之vTaskStartScheduler实现分析

  • 1 FreeRTOS源码下载地址
  • 2 函数接口
  • 2.1 函数接口
  • 2.2 函数参数简介
  • 3 vTaskDelete的调用关系
    • 3.1 调用关系
    • 3.2 调用关系示意图
  • 4 函数源码分析
    • 4.1 vTaskStartScheduler
    • 4.2 prvCreateIdleTasks
      • 4.2.1 prvCreateIdleTasks
      • 4.2.2 xTaskCreate
  • 4.3 xTimerCreateTimerTask
    • 4.4 xTaskCreateAffinitySet
    • 4.5 xPortStartScheduler
    • 4.6 vPortRestoreTaskContext
    • 4.7 portRESTORE_CONTEXT
    • 4.8 pxPortInitialiseStack

1 FreeRTOS源码下载地址

https://www.freertos.org/
在这里插入图片描述

2 函数接口

2.1 函数接口

void vTaskStartScheduler( void )

2.2 函数参数简介

参数 - 无

3 vTaskDelete的调用关系

3.1 调用关系

|- vTaskStartScheduler
	|- prvCreateIdleTasks()
		|- xTaskCreate( pxIdleTaskFunction, ...)
			|- prvCreateTask
				|- pxStack = pvPortMallocStack( ( ( ( size_t ) uxStackDepth ) * sizeof( StackType_t ) ) );
				|- pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
				|- prvInitialiseNewTask
					|- vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
					|- vListInitialiseItem( &( pxNewTCB->xEventListItem ) );
					|- pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
					|- *pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
			|- prvAddNewTaskToReadyList
				|- prvInitialiseTaskLists
				|- prvAddTaskToReadyList
					|- listINSERT_END( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) );
	|- xTimerCreateTimerTask()
		|- xTaskCreateAffinitySet
			|- prvCreateTask
					|- pxStack = pvPortMallocStack( ( ( ( size_t ) uxStackDepth ) * sizeof( StackType_t ) ) );
					|- pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
					|- prvInitialiseNewTask
						|- vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
						|- vListInitialiseItem( &( pxNewTCB->xEventListItem ) );
						|- pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
						|- *pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
				|- prvAddNewTaskToReadyList
					|- prvInitialiseTaskLists
					|- prvAddTaskToReadyList
						|- listINSERT_END( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) )
			|- pxNewTCB->uxCoreAffinityMask = uxCoreAffinityMask;
			|- prvAddNewTaskToReadyList( pxNewTCB );
	|- xPortStartScheduler()
		|- vPortRestoreTaskContext
			|- portRESTORE_CONTEXT

3.2 调用关系示意图

4 函数源码分析

4.1 vTaskStartScheduler

  • xReturn = prvCreateIdleTasks(); 创建idle线程
  • xReturn = xTimerCreateTimerTask(); 创建timer线程
  • xSchedulerRunning = pdTRUE; 将当前系统的运行状态设置为pdTRUE
  • ( void ) xPortStartScheduler(); 开始调度,这个和当前处理器的架构相关的,和也栈结构的设置相关。
void vTaskStartScheduler( void )
{
    BaseType_t xReturn;

    traceENTER_vTaskStartScheduler();

    #if ( configUSE_CORE_AFFINITY == 1 ) && ( configNUMBER_OF_CORES > 1 )
    {
        /* Sanity check that the UBaseType_t must have greater than or equal to
         * the number of bits as confNUMBER_OF_CORES. */
        configASSERT( ( sizeof( UBaseType_t ) * taskBITS_PER_BYTE ) >= configNUMBER_OF_CORES );
    }
    #endif /* #if ( configUSE_CORE_AFFINITY == 1 ) && ( configNUMBER_OF_CORES > 1 ) */

    xReturn = prvCreateIdleTasks();

    #if ( configUSE_TIMERS == 1 )
    {
        if( xReturn == pdPASS )
        {
            xReturn = xTimerCreateTimerTask();
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }
    #endif /* configUSE_TIMERS */

    if( xReturn == pdPASS )
    {
        /* freertos_tasks_c_additions_init() should only be called if the user
         * definable macro FREERTOS_TASKS_C_ADDITIONS_INIT() is defined, as that is
         * the only macro called by the function. */
        #ifdef FREERTOS_TASKS_C_ADDITIONS_INIT
        {
            freertos_tasks_c_additions_init();
        }
        #endif

        /* Interrupts are turned off here, to ensure a tick does not occur
         * before or during the call to xPortStartScheduler().  The stacks of
         * the created tasks contain a status word with interrupts switched on
         * so interrupts will automatically get re-enabled when the first task
         * starts to run. */
        portDISABLE_INTERRUPTS();

        #if ( configUSE_C_RUNTIME_TLS_SUPPORT == 1 )
        {
            /* Switch C-Runtime's TLS Block to point to the TLS
             * block specific to the task that will run first. */
            configSET_TLS_BLOCK( pxCurrentTCB->xTLSBlock );
        }
        #endif

        xNextTaskUnblockTime = portMAX_DELAY;
        xSchedulerRunning = pdTRUE;
        xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT;

        /* If configGENERATE_RUN_TIME_STATS is defined then the following
         * macro must be defined to configure the timer/counter used to generate
         * the run time counter time base.   NOTE:  If configGENERATE_RUN_TIME_STATS
         * is set to 0 and the following line fails to build then ensure you do not
         * have portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() defined in your
         * FreeRTOSConfig.h file. */
        portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();

        traceTASK_SWITCHED_IN();

        traceSTARTING_SCHEDULER( xIdleTaskHandles );

        /* Setting up the timer tick is hardware specific and thus in the
         * portable interface. */

        /* The return value for xPortStartScheduler is not required
         * hence using a void datatype. */
        ( void ) xPortStartScheduler();

        /* In most cases, xPortStartScheduler() will not return. If it
         * returns pdTRUE then there was not enough heap memory available
         * to create either the Idle or the Timer task. If it returned
         * pdFALSE, then the application called xTaskEndScheduler().
         * Most ports don't implement xTaskEndScheduler() as there is
         * nothing to return to. */
    }
    else
    {
        /* This line will only be reached if the kernel could not be started,
         * because there was not enough FreeRTOS heap to create the idle task
         * or the timer task. */
        configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY );
    }

    /* Prevent compiler warnings if INCLUDE_xTaskGetIdleTaskHandle is set to 0,
     * meaning xIdleTaskHandles are not used anywhere else. */
    ( void ) xIdleTaskHandles;

    /* OpenOCD makes use of uxTopUsedPriority for thread debugging. Prevent uxTopUsedPriority
     * from getting optimized out as it is no longer used by the kernel. */
    ( void ) uxTopUsedPriority;

    traceRETURN_vTaskStartScheduler();
}

4.2 prvCreateIdleTasks

4.2.1 prvCreateIdleTasks

  • pxIdleTaskFunction = prvIdleTask; 设置当前idle任务的处理函数为prvIdleTask
  • 创建idle task
xReturn = xTaskCreate( pxIdleTaskFunction,
                                   cIdleName,
                                   configMINIMAL_STACK_SIZE,
                                   ( void * ) NULL,
                                   portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */
                                   &xIdleTaskHandles[ xCoreID ] );
static BaseType_t prvCreateIdleTasks( void )
{
    BaseType_t xReturn = pdPASS;
    BaseType_t xCoreID;
    char cIdleName[ configMAX_TASK_NAME_LEN ];
    TaskFunction_t pxIdleTaskFunction = NULL;
    BaseType_t xIdleTaskNameIndex;

    for( xIdleTaskNameIndex = ( BaseType_t ) 0; xIdleTaskNameIndex < ( BaseType_t ) configMAX_TASK_NAME_LEN; xIdleTaskNameIndex++ )
    {
        cIdleName[ xIdleTaskNameIndex ] = configIDLE_TASK_NAME[ xIdleTaskNameIndex ];

        /* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter than
         * configMAX_TASK_NAME_LEN characters just in case the memory after the
         * string is not accessible (extremely unlikely). */
        if( cIdleName[ xIdleTaskNameIndex ] == ( char ) 0x00 )
        {
            break;
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }

    /* Add each idle task at the lowest priority. */
    for( xCoreID = ( BaseType_t ) 0; xCoreID < ( BaseType_t ) configNUMBER_OF_CORES; xCoreID++ )
    {
        #if ( configNUMBER_OF_CORES == 1 )
        {
            pxIdleTaskFunction = prvIdleTask;
        }
        #else /* #if (  configNUMBER_OF_CORES == 1 ) */
        {
            /* In the FreeRTOS SMP, configNUMBER_OF_CORES - 1 passive idle tasks
             * are also created to ensure that each core has an idle task to
             * run when no other task is available to run. */
            if( xCoreID == 0 )
            {
                pxIdleTaskFunction = prvIdleTask;
            }
            else
            {
                pxIdleTaskFunction = prvPassiveIdleTask;
            }
        }
        #endif /* #if (  configNUMBER_OF_CORES == 1 ) */

        /* Update the idle task name with suffix to differentiate the idle tasks.
         * This function is not required in single core FreeRTOS since there is
         * only one idle task. */
        #if ( configNUMBER_OF_CORES > 1 )
        {
            /* Append the idle task number to the end of the name if there is space. */
            if( xIdleTaskNameIndex < ( BaseType_t ) configMAX_TASK_NAME_LEN )
            {
                cIdleName[ xIdleTaskNameIndex ] = ( char ) ( xCoreID + '0' );

                /* And append a null character if there is space. */
                if( ( xIdleTaskNameIndex + 1 ) < ( BaseType_t ) configMAX_TASK_NAME_LEN )
                {
                    cIdleName[ xIdleTaskNameIndex + 1 ] = '\0';
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        #endif /* if ( configNUMBER_OF_CORES > 1 ) */

        #if ( configSUPPORT_STATIC_ALLOCATION == 1 )
        {
            StaticTask_t * pxIdleTaskTCBBuffer = NULL;
            StackType_t * pxIdleTaskStackBuffer = NULL;
            configSTACK_DEPTH_TYPE uxIdleTaskStackSize;

            /* The Idle task is created using user provided RAM - obtain the
             * address of the RAM then create the idle task. */
            #if ( configNUMBER_OF_CORES == 1 )
            {
                vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &uxIdleTaskStackSize );
            }
            #else
            {
                if( xCoreID == 0 )
                {
                    vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &uxIdleTaskStackSize );
                }
                else
                {
                    vApplicationGetPassiveIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &uxIdleTaskStackSize, ( BaseType_t ) ( xCoreID - 1 ) );
                }
            }
            #endif /* if ( configNUMBER_OF_CORES == 1 ) */
            xIdleTaskHandles[ xCoreID ] = xTaskCreateStatic( pxIdleTaskFunction,
                                                             cIdleName,
                                                             uxIdleTaskStackSize,
                                                             ( void * ) NULL,
                                                             portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */
                                                             pxIdleTaskStackBuffer,
                                                             pxIdleTaskTCBBuffer );

            if( xIdleTaskHandles[ xCoreID ] != NULL )
            {
                xReturn = pdPASS;
            }
            else
            {
                xReturn = pdFAIL;
            }
        }
        #else /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */
        {
            /* The Idle task is being created using dynamically allocated RAM. */
            xReturn = xTaskCreate( pxIdleTaskFunction,
                                   cIdleName,
                                   configMINIMAL_STACK_SIZE,
                                   ( void * ) NULL,
                                   portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */
                                   &xIdleTaskHandles[ xCoreID ] );
        }
        #endif /* configSUPPORT_STATIC_ALLOCATION */

        /* Break the loop if any of the idle task is failed to be created. */
        if( xReturn != pdPASS )
        {
            break;
        }
        else
        {
            #if ( configNUMBER_OF_CORES == 1 )
            {
                mtCOVERAGE_TEST_MARKER();
            }
            #else
            {
                /* Assign idle task to each core before SMP scheduler is running. */
                xIdleTaskHandles[ xCoreID ]->xTaskRunState = xCoreID;
                pxCurrentTCBs[ xCoreID ] = xIdleTaskHandles[ xCoreID ];
            }
            #endif
        }
    }

    return xReturn;
}

4.2.2 xTaskCreate

xTaskCreate的处理流程可以参考FreeRTOS之xTaskCreate,其调用流程如下所示:

|- xTaskCreate
	|- prvCreateTask
		|- pxStack = pvPortMallocStack( ( ( ( size_t ) uxStackDepth ) * sizeof( StackType_t ) ) );
		|- pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
		|- prvInitialiseNewTask
			|- vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
			|- vListInitialiseItem( &( pxNewTCB->xEventListItem ) );
			|- pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
			|- *pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
	|- prvAddNewTaskToReadyList
		|- prvInitialiseTaskLists
		|- prvAddTaskToReadyList
			|- listINSERT_END( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) );

4.3 xTimerCreateTimerTask

  • 多核并设置亲和性:创建Timer 任务,同时做亲和性设置,该处理是在多核的情况下做的处理,如果是单核或者没有开启亲和性的配置则会调用xTaskCreate去创建Timer任务。
xReturn = xTaskCreateAffinitySet( prvTimerTask,
												  configTIMER_SERVICE_TASK_NAME,
												  configTIMER_TASK_STACK_DEPTH,
												  NULL,
												  ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT,
												  configTIMER_SERVICE_TASK_CORE_AFFINITY,
												  &xTimerTaskHandle );
  • 单核或者未开启亲和性设置:
xReturn = xTaskCreate( prvTimerTask,
									   configTIMER_SERVICE_TASK_NAME,
									   configTIMER_TASK_STACK_DEPTH,
									   NULL,
									   ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT,
									   &xTimerTaskHandle );
BaseType_t xTimerCreateTimerTask( void )
{
	BaseType_t xReturn = pdFAIL;

	traceENTER_xTimerCreateTimerTask();

	/* This function is called when the scheduler is started if
	 * configUSE_TIMERS is set to 1.  Check that the infrastructure used by the
	 * timer service task has been created/initialised.  If timers have already
	 * been created then the initialisation will already have been performed. */
	prvCheckForValidListAndQueue();

	if( xTimerQueue != NULL )
	{
		#if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) )
		{
			#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
			{
				StaticTask_t * pxTimerTaskTCBBuffer = NULL;
				StackType_t * pxTimerTaskStackBuffer = NULL;
				configSTACK_DEPTH_TYPE uxTimerTaskStackSize;

				vApplicationGetTimerTaskMemory( &pxTimerTaskTCBBuffer, &pxTimerTaskStackBuffer, &uxTimerTaskStackSize );
				xTimerTaskHandle = xTaskCreateStaticAffinitySet( prvTimerTask,
																 configTIMER_SERVICE_TASK_NAME,
																 uxTimerTaskStackSize,
																 NULL,
																 ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT,
																 pxTimerTaskStackBuffer,
																 pxTimerTaskTCBBuffer,
																 configTIMER_SERVICE_TASK_CORE_AFFINITY );

				if( xTimerTaskHandle != NULL )
				{
					xReturn = pdPASS;
				}
			}
			#else /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */
			{
				xReturn = xTaskCreateAffinitySet( prvTimerTask,
												  configTIMER_SERVICE_TASK_NAME,
												  configTIMER_TASK_STACK_DEPTH,
												  NULL,
												  ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT,
												  configTIMER_SERVICE_TASK_CORE_AFFINITY,
												  &xTimerTaskHandle );
			}
			#endif /* configSUPPORT_STATIC_ALLOCATION */
		}
		#else /* #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) */
		{
			#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
			{
				StaticTask_t * pxTimerTaskTCBBuffer = NULL;
				StackType_t * pxTimerTaskStackBuffer = NULL;
				configSTACK_DEPTH_TYPE uxTimerTaskStackSize;

				vApplicationGetTimerTaskMemory( &pxTimerTaskTCBBuffer, &pxTimerTaskStackBuffer, &uxTimerTaskStackSize );
				xTimerTaskHandle = xTaskCreateStatic( prvTimerTask,
													  configTIMER_SERVICE_TASK_NAME,
													  uxTimerTaskStackSize,
													  NULL,
													  ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT,
													  pxTimerTaskStackBuffer,
													  pxTimerTaskTCBBuffer );

				if( xTimerTaskHandle != NULL )
				{
					xReturn = pdPASS;
				}
			}
			#else /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */
			{
				xReturn = xTaskCreate( prvTimerTask,
									   configTIMER_SERVICE_TASK_NAME,
									   configTIMER_TASK_STACK_DEPTH,
									   NULL,
									   ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT,
									   &xTimerTaskHandle );
			}
			#endif /* configSUPPORT_STATIC_ALLOCATION */
		}
		#endif /* #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) */
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}

	configASSERT( xReturn );

	traceRETURN_xTimerCreateTimerTask
上一篇:鸿蒙面试 --- 性能优化(精简版)