FreeRTOS任务创建、删除、挂起、恢复源码分析

目录

1.任务控制块(结构体TCB_t)

2.FreeRTOS任务创建源码分析

3.FreeRTOS任务删除源码分析

4.FreeRTOS任务挂起源码分析

5.FreeRTOS任务恢复源码分析

6.任务API总结(部分)


1.任务控制块(结构体TCB_t)

typedef struct tskTaskControlBlock 			/* The old naming convention is used to prevent breaking kernel aware debuggers. */
{
    //任务堆栈栈顶
	volatile StackType_t	*pxTopOfStack;	/*< Points to the location of the last item placed on the tasks stack.  THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */
    //状态列表项
	ListItem_t			xStateListItem;	/*< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */
	//事件列表项
    ListItem_t			xEventListItem;		/*< Used to reference a task from an event list. */
	//任务优先级
    UBaseType_t			uxPriority;			/*< The priority of the task.  0 is the lowest priority. */
	//任务栈起始地址
    StackType_t			*pxStack;			/*< Points to the start of the stack. */
	//任务名称
    char				pcTaskName[ configMAX_TASK_NAME_LEN ];/*< Descriptive name given to the task when created.  Facilitates debugging only. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
} tskTCB;

typedef tskTCB TCB_t;

2.FreeRTOS任务创建源码分析

        2.1.分配任务堆栈空间分配任务控制块内存空间

                先申请任务栈再申请TCB,任务栈地址<TCB地址,这种情况下,随着程序运行,任务栈栈顶地址往低地址方向走,即使任务栈空间不足,也不会触碰到TCB区域,这样相对安全一些。

        2.2.初始化任务控制块初始化任务堆栈

        2.3.添加任务到就绪列表

在这里插入图片描述

 

	BaseType_t xTaskCreate(	TaskFunction_t pxTaskCode,
							const char * const pcName,		/*lint !e971 Unqualified char types are allowed for strings and single characters only. */
							const configSTACK_DEPTH_TYPE usStackDepth,
							void * const pvParameters,
							UBaseType_t uxPriority,
							TaskHandle_t * const pxCreatedTask )
	{
	TCB_t *pxNewTCB;
	BaseType_t xReturn;
        
        //指硬件平台栈增长方式
        //Cortex-M4权威指南4.4.3
        //Cortex-M处理器使用的栈模型被称为“满递减”
        //#define portSTACK_GROWTH			( -1 )表示满减栈
		/* If the stack grows down then allocate the stack then the TCB so the stack
		does not grow into the TCB.  Likewise if the stack grows up then allocate
		the TCB then the stack. */
		#if( portSTACK_GROWTH > 0 )
		{
		}
		#else /* portSTACK_GROWTH */
		{
		StackType_t *pxStack;        //这个指针用于存储栈地址

			/* Allocate space for the stack used by the task being created. */
            //任务栈内存分配
			pxStack = pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation is the stack. */

			if( pxStack != NULL )    //任务栈内存分配成功
			{
                //任务控制块内存分配
				/* Allocate space for the TCB. */
				pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e9087 !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack, and the first member of TCB_t is always a pointer to the task's stack. */

				if( pxNewTCB != NULL )    //任务控制块内存分配成功
				{
                    //TCB中存储栈地址
					/* Store the stack location in the TCB. */
					pxNewTCB->pxStack = pxStack;
				}
				else
				{
                    //TCB创建失败,释放栈空间
					/* The stack cannot be used as the TCB was not created.  Free
					it again. */
					vPortFree( pxStack );
				}
			}
			else        //任务栈内存分配失败
			{
				pxNewTCB = NULL;
			}
		}
		#endif /* portSTACK_GROWTH */

		if( pxNewTCB != NULL )        //任务控制块内存分配成功
		{
            //新建任务初始化,具体函数实现见下一个代码段
			prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
            //把任务添加到就绪列表中
			prvAddNewTaskToReadyList( pxNewTCB );
			xReturn = pdPASS;
		}
		else
		{
			xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
		}

		return xReturn;
	}
static void prvInitialiseNewTask( 	TaskFunction_t pxTaskCode,
									const char * const pcName,		/*lint !e971 Unqualified char types are allowed for strings and single characters only. */
									const uint32_t ulStackDepth,
									void * const pvParameters,
									UBaseType_t uxPriority,
									TaskHandle_t * const pxCreatedTask,
									TCB_t *pxNewTCB,
									const MemoryRegion_t * const xRegions )
{
StackType_t *pxTopOfStack;
UBaseType_t x;

    //计算栈顶的地址
	/* Calculate the top of stack address.  This depends on whether the stack
	grows from high memory to low (as per the 80x86) or vice versa.
	portSTACK_GROWTH is used to make the result positive or negative as required
	by the port. */
	#if( portSTACK_GROWTH < 0 )
	{
        //把栈空间的高地址分配给栈顶
		pxTopOfStack = &( pxNewTCB->pxStack[ ulStackDepth - ( uint32_t ) 1 ] );
        //栈对齐,栈要8字节对齐
		pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); /*lint !e923 !e9033 !e9078 MISRA exception.  Avoiding casts between pointers and integers is not practical.  Size differences accounted for using portPOINTER_SIZE_TYPE type.  Checked by assert(). */
          
        //检查是否有错误
		/* Check the alignment of the calculated top of stack is correct. */
		configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );

	}
	#else /* portSTACK_GROWTH */
	{
	}
	#endif /* portSTACK_GROWTH */
    
    //在TCB中存储任务名称
	/* Store the task name in the TCB. */
	if( pcName != NULL )
	{
		for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
		{
			pxNewTCB->pcTaskName[ x ] = pcName[ x ];

			/* 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( pcName[ x ] == ( char ) 0x00 )
			{
				break;
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}

		/* Ensure the name string is terminated in the case that the string length
		was greater or equal to configMAX_TASK_NAME_LEN. */
		pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';
	}
	else
	{
		/* The task has not been given a name, so just ensure there is a NULL
		terminator when it is read out. */
		pxNewTCB->pcTaskName[ 0 ] = 0x00;
	}
    
    //判断任务分配的优先级是否大于最大值
	/* This is used as an array index so must ensure it's not too large.  First
	remove the privilege bit if one is present. */
	if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
	{
		uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}
    
    //在TCB中存储任务优先级
	pxNewTCB->uxPriority = uxPriority;

    //任务状态表、事件表初始化
	vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
	vListInitialiseItem( &( pxNewTCB->xEventListItem ) );

    //任务控制块链接到任务状态表中
	/* Set the pxNewTCB as a link back from the ListItem_t.  This is so we can get
	back to	the containing TCB from a generic item in a list. */
	listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );
    
    //任务控制块链接到任务事件表中
	/* Event lists are always in priority order. */
	listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
	listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB );


    
	/* Initialize the TCB stack to look as if the task was already running,
	but had been interrupted by the scheduler.  The return address is set
	to the start of the task function. Once the stack has been initialised
	the top of stack variable is updated. */
    //#define portUSING_MPU_WRAPPERS 0
	#if( portUSING_MPU_WRAPPERS == 1 )
	{
	}
	#else /* portUSING_MPU_WRAPPERS */
	{
        //任务堆栈初始化,之后返回任务栈顶
	    pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );

	}
	#endif /* portUSING_MPU_WRAPPERS */

	if( pxCreatedTask != NULL )
	{
        //赋值任务句柄(就是TCB)
		/* Pass the handle out in an anonymous way.  The handle can be used to
		change the created task's priority, delete the created task, etc.*/
		*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}
}
//M4权威指南第八章节,分析异常处理
//任务调度其实就是通过CPU内核异常处理实现的
StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{
	/* Simulate the stack frame as it would be created by a context switch
	interrupt. */

	/* Offset added to account for the way the MCU uses the stack on entry/exit
	of interrupts, and to ensure alignment. */
	pxTopOfStack--;
    //入栈程序状态寄存器
	*pxTopOfStack = portINITIAL_XPSR;	/* xPSR */
	pxTopOfStack--;
    //入栈PC指针
	*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK;	/* PC */
	pxTopOfStack--;
    //入栈LR链接寄存器
	*pxTopOfStack = ( StackType_t ) prvTaskExitError;	/* LR */
    
    //R1,R2,R3,R12不需要初始化
	/* Save code space by skipping register initialisation. */
	pxTopOfStack -= 5;	/* R12, R3, R2 and R1. */
    //R0作为传参入栈
	*pxTopOfStack = ( StackType_t ) pvParameters;	/* R0 */

	/* A save method is being used that requires each task to maintain its
	own exec return value. */
	pxTopOfStack--;
    //异常返回值入栈    返回值是确定程序使用的栈地址是哪一个  MSP还是PSP
	*pxTopOfStack = portINITIAL_EXC_RETURN;
    //R4-R11不初始化
	pxTopOfStack -= 8;	/* R11, R10, R9, R8, R7, R6, R5 and R4. */
    //最终返回栈顶值
	return pxTopOfStack;
}

3.FreeRTOS任务删除源码分析

        3.1.从就绪表中删除

        3.2.从事件表中删除

        3.3.释放任务控制块释放任务堆栈内存

        3.4.开始任务调度

	void vTaskDelete( TaskHandle_t xTaskToDelete )
	{
	TCB_t *pxTCB;
        //进入临界段
		taskENTER_CRITICAL();
		{
            //获取任务控制块————参数传入的是任务句柄
            //判断是任务本身还是其他任务
            //如果是当前任务,传入的参数为NULL
			/* If null is passed in here then it is the calling task that is
			being deleted. */
			pxTCB = prvGetTCBFromHandle( xTaskToDelete );
            
            //从就绪列表中移除
			/* Remove task from the ready/delayed list. */
			if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
			{
				taskRESET_READY_PRIORITY( pxTCB->uxPriority );
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
            
            //从事件列表中移除
			/* Is the task waiting on an event also? */
			if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
			{
				( void ) uxListRemove( &( pxTCB->xEventListItem ) );
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}

			/* Increment the uxTaskNumber also so kernel aware debuggers can
			detect that the task lists need re-generating.  This is done before
			portPRE_TASK_DELETE_HOOK() as in the Windows port that macro will
			not return. */
			uxTaskNumber++;
            
            //如果删除的是当前任务
			if( pxTCB == pxCurrentTCB )
			{
                //不能在此删除,要在空闲任务中删除
                //把任务添加到等待删除的任务列表中
				/* A task is deleting itself.  This cannot complete within the
				task itself, as a context switch to another task is required.
				Place the task in the termination list.  The idle task will
				check the termination list and free up any memory allocated by
				the scheduler for the TCB and stack of the deleted task. */
				vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );
                
                //给空闲任务一个标记
				/* Increment the ucTasksDeleted variable so the idle task knows
				there is a task that has been deleted and that it should therefore
				check the xTasksWaitingTermination list. */
				++uxDeletedTasksWaitingCleanUp;
                
                //钩子函数,需要用户自己实现
				/* Call the delete hook before portPRE_TASK_DELETE_HOOK() as
				portPRE_TASK_DELETE_HOOK() does not return in the Win32 port. */
				traceTASK_DELETE( pxTCB );

				/* The pre-delete hook is primarily for the Windows simulator,
				in which Windows specific clean up operations are performed,
				after which it is not possible to yield away from this task -
				hence xYieldPending is used to latch that a context switch is
				required. */
				portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending );
			}
			else    //如果删除的不是当前任务
			{
                //直接删除任务控制块
				--uxCurrentNumberOfTasks;
				traceTASK_DELETE( pxTCB );
                //复位任务锁定时间
                //操作系统根据最新的时间进行调度,所以删除任务后,要更新锁定时间
				prvDeleteTCB( pxTCB );

				/* Reset the next expected unblock time in case it referred to
				the task that has just been deleted. */
				prvResetNextTaskUnblockTime();
			}
		}
        //退出临界段
		taskEXIT_CRITICAL();
        
        //判断调度器是否开启
		/* Force a reschedule if it is the currently running task that has just
		been deleted. */
		if( xSchedulerRunning != pdFALSE )    //调度器开启中
		{
			if( pxTCB == pxCurrentTCB )    //如果是删除任务本身
			{
                //马上进行任务调度(释放CPU的使用权)
				configASSERT( uxSchedulerSuspended == 0 );
				portYIELD_WITHIN_API();
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
	}

4.FreeRTOS任务挂起源码分析

        4.1.从就绪表中删除

        4.2.从事件表中删除

        4.3.添加任务到挂起列表中

        4.4.开始任务调度

    void vTaskSuspend( TaskHandle_t xTaskToSuspend )
	{
	TCB_t *pxTCB;
        //进入临界段
		taskENTER_CRITICAL();
		{
            //获取任务控制块
			/* If null is passed in here then it is the running task that is
			being suspended. */
			pxTCB = prvGetTCBFromHandle( xTaskToSuspend );

            //从就绪列表中移除
			/* Remove task from the ready/delayed list and place in the
			suspended list. */
			if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
			{
				taskRESET_READY_PRIORITY( pxTCB->uxPriority );
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
            
            //从事件列表中移除
			/* Is the task waiting on an event also? */
			if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
			{
				( void ) uxListRemove( &( pxTCB->xEventListItem ) );
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
            
            //把任务添加到挂起列表中
			vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) );
		}
        //退出临界段
		taskEXIT_CRITICAL();
        
        //判断调度器是否开启
		if( xSchedulerRunning != pdFALSE )
		{
            //更新下个任务的锁定时间
			/* Reset the next expected unblock time in case it referred to the
			task that is now in the Suspended state. */
			taskENTER_CRITICAL();
			{
				prvResetNextTaskUnblockTime();
			}
			taskEXIT_CRITICAL();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}

		if( pxTCB == pxCurrentTCB )        //如果是当前任务
		{
			if( xSchedulerRunning != pdFALSE )    //如果调度器已开启
			{
                //直接开始任务调度,释放CPU的使用权
				/* The current task has just been suspended. */
				configASSERT( uxSchedulerSuspended == 0 );
				portYIELD_WITHIN_API();
			}
			else        //如果调度器没有开启
			{
                //读取当前任务挂起列表的长度
				/* The scheduler is not running, but the task that was pointed
				to by pxCurrentTCB has just been suspended and pxCurrentTCB
				must be adjusted to point to a different task. */
				if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks ) /*lint !e931 Right has no side effect, just volatile. */
				{
                    //如果挂起列表已经把所有任务挂起,把当前任务控制块赋值为NULL,不让任务控制块再使用了
					/* No other tasks are ready, so set pxCurrentTCB back to
					NULL so when the next task is created pxCurrentTCB will
					be set to point to it no matter what its relative priority
					is. */
					pxCurrentTCB = NULL;
				}
				else
				{
                    //任务上下文切换
                    //即在就绪列表中找到优先级最高的任务进行调度
					vTaskSwitchContext();
				}
			}
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}

5.FreeRTOS任务恢复源码分析

        5.1.从挂起表中删除

        5.2.添加任务到就绪列表中

        5.3.开始任务调度

	void vTaskResume( TaskHandle_t xTaskToResume )
	{
    //获取要恢复的任务控制块
	TCB_t * const pxTCB = xTaskToResume;
        
        //检查
		/* It does not make sense to resume the calling task. */
		configASSERT( xTaskToResume );

        //既然要使用任务控制块,肯定不能是NULL,也不能是当前的任务
		/* The parameter cannot be NULL as it is impossible to resume the
		currently executing task. */
		if( ( pxTCB != pxCurrentTCB ) && ( pxTCB != NULL ) )
		{
            //进入临界段
			taskENTER_CRITICAL();
			{
                //判断任务是否已经挂起
				if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE )
				{
                    //从挂起列表中移除
					/* The ready list can be accessed even if the scheduler is
					suspended because this is inside a critical section. */
					( void ) uxListRemove(  &( pxTCB->xStateListItem ) );
                    //添加任务到就绪列表
					prvAddTaskToReadyList( pxTCB );
                
                    //判断要恢复的任务是否大于当前任务的优先级
                    //如果大于,释放CPU的使用权,开始内核调度
                    //前提条件是已经使能了抢占式调度器
					/* A higher priority task may have just been resumed. */
					if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
					{
						/* This yield may not cause the task just resumed to run,
						but will leave the lists in the correct state for the
						next yield. */
						taskYIELD_IF_USING_PREEMPTION();
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
            //退出临界段
			taskEXIT_CRITICAL();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}

6.任务API总结(部分)

API名称API说明
xTaskCreate动态创建任务
xTaskCreateStatic静态创建任务
vTaskDelete删除任务
vTaskSuspend挂起任务
vTaskResume恢复任务
xTaskResumeFromISR在中断中恢复任务
uxTaskPriorityGet获取任务优先级
vTaskPrioritySet设置任务优先级
vTaskDelay

延时任务

Functions are prefixed with both the type they return and the file they are defined in. For example:
1.vTaskPrioritySet() returns a void and is defined within task.c.
2.xQueueReceive() returns a variable of type BaseType_t and is defined within queue.c.
3.vSemaphoreCreateBinary() returns a void and is defined within semphr.h.
File scope (private) functions are prefixed with ‘prv’.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值