【FreeRTOS学习03】小白都能懂的Task Management 任务管理基本概念介绍

在FreeRTOS中,线程的术语又可以被称之为任务,或许这样更加合适,本文将介绍任务的创建/删除,任务参数的使用,以及任务优先级;

1 软实时和硬实时

硬实时系统的任务运行正确性与响应时限是紧密相关的,一旦超过时限将导致严重的后果,比如导弹控制系统、高铁自动驾驶系统等,都是需要严格的响应时限的。
软实时系统中,虽然也存在时限指标,但是如果输出响应超过时限,一般不会造成严重后果,比如Windows桌面任务,DVD播放机的视频播放。
大多数嵌入式系统不仅能满足硬实时要求,也能满足软实时要求。
软实时

  • WindowsLinux系统通常为软实时,当然有补丁可以将内核做成硬实时的系统,不过商用没有这么做的。

硬实时

  • VxWorksuCOSFreeRTOSWinCERT-thread等实时系统;

2 任务概述

2.1 基本写法

FreeRTOS多任务的实时系统,其最基本的运行单元为任务,其表示形式为由C语言函数实现的,该函数原型要求必须返回 void,并且带一个 void 类型指针的参数;具体如下所示;

void ATaskFunc(void *args);

每个任务都是在自己权限范围内的一个小程序。其具有程序入口,通常会运行在一个死循环中,也不会退出,具体如下;

void ATaskFunc(void *args){

	while(1){
		//TODO 
	}
}

FreeRTOS 任务不允许以任何方式从实现函数中返回——它们绝不能有一条return语句,也不能执行到函数末尾,如果不再需要,则在任务中调用删除任务的API,具体如下所示;

```c
void ATaskFunc(void *args){

	vTaskDelete( NULL );
}

2.2 TCB

TCB为任务控制块,或者是线程控制块,另外操作系统中还有PCB为进程控制块,主要封装了一个任务在系统调度中所需要的所有资源,FreeRTOSTCB的成员,具体如下所示;

任务状态如下所示;

typedef struct xTASK_STATUS
{
	/* The handle of the task to which the rest of the information in the structure relates. */
	TaskHandle_t xHandle;			
	/* A pointer to the task's name.*/ 
	const char *pcTaskName;			
	/* A number unique to the task. */	
	UBaseType_t xTaskNumber;		
	/* The state in which the task existed when the structure was populated. */
	eTaskState eCurrentState;		
	/* The priority at which the task was running (may be inherited) when the structure was populated. */
	UBaseType_t uxCurrentPriority;	
	UBaseType_t uxBasePriority;		
	uint32_t ulRunTimeCounter;		
	StackType_t *pxStackBase;		
	uint16_t usStackHighWaterMark;	
} TaskStatus_t;

每一个任务都会拥有一个自己的TCB,具体如下图所示;

typedef struct tskTaskControlBlock
{
	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. */

	#if ( portUSING_MPU_WRAPPERS == 1 )
		xMPU_SETTINGS	xMPUSettings;		/*< The MPU settings are defined as part of the port layer.  THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */
	#endif

	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. */

	#if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )
		StackType_t		*pxEndOfStack;		/*< Points to the highest valid address for the stack. */
	#endif

	#if ( portCRITICAL_NESTING_IN_TCB == 1 )
		UBaseType_t		uxCriticalNesting;	/*< Holds the critical section nesting depth for ports that do not maintain their own count in the port layer. */
	#endif

	#if ( configUSE_TRACE_FACILITY == 1 )
		UBaseType_t		uxTCBNumber;		/*< Stores a number that increments each time a TCB is created.  It allows debuggers to determine when a task has been deleted and then recreated. */
		UBaseType_t		uxTaskNumber;		/*< Stores a number specifically for use by third party trace code. */
	#endif

	#if ( configUSE_MUTEXES == 1 )
		UBaseType_t		uxBasePriority;		/*< The priority last assigned to the task - used by the priority inheritance mechanism. */
		UBaseType_t		uxMutexesHeld;
	#endif

	#if ( configUSE_APPLICATION_TASK_TAG == 1 )
		TaskHookFunction_t pxTaskTag;
	#endif

	#if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 )
		void			*pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ];
	#endif

	#if( configGENERATE_RUN_TIME_STATS == 1 )
		uint32_t		ulRunTimeCounter;	/*< Stores the amount of time the task has spent in the Running state. */
	#endif

	#if ( configUSE_NEWLIB_REENTRANT == 1 )
		struct	_reent xNewLib_reent;
	#endif

	#if( configUSE_TASK_NOTIFICATIONS == 1 )
		volatile uint32_t ulNotifiedValue;
		volatile uint8_t ucNotifyState;
	#endif

	/* See the comments above the definition of
	tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */
	#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 Macro has been consolidated for readability reasons. */
		uint8_t	ucStaticallyAllocated; 		/*< Set to pdTRUE if the task is a statically allocated to ensure no attempt is made to free the memory. */
	#endif

	#if( INCLUDE_xTaskAbortDelay == 1 )
		uint8_t ucDelayAborted;
	#endif

} tskTCB;

3 任务状态

任务顶层存在两种状态,运行态非运行态
但是非运行的任务状态又可以分为:堵塞状态 / Blocked,挂起状态 / Suspend,就绪状态 / Ready,下面简单做一下介绍;

  • 运行状态 / Running
    运行态的任务完全占用CPU的使用权,如果当前CPU只有一个内核,那么在某个时刻只能运行一个任务,就是所谓的单核单线程;

  • 堵塞状态 / Blocked
    用户可以主动调用vTaskDelay(T)将任务进入堵塞状态,直到任务堵塞时间已经达到T;或者该任务在等待队列,信号量,事件组,通知或信号量事件时,也将处于堵塞状态;处于堵塞状态的任务不再占用CPU,同样也不能直接进入运行状态,而是先进入就绪状态

  • 挂起状态 / Suspend
    任何状态的下的任务都可以通过调用vTaskSuspend函数进入挂起状态,并且无法直接进入运行态,只能通过调用xTaskResume函数进入就绪状态

  • 就绪状态 / Ready
    被抢占的任务将处于就绪状态,挂起的任务被回复的会处于就绪状态,堵塞的任务收到相应事件也会处于就绪状态,如果当前没有更高优先级的任务处于运行,则当前就绪状态的任务进入运行状态

其主要的状态转换关系如下图所示;

在这里插入图片描述
FreeRTOS使用一个枚举封装了任务的状态,具体如下所示;

typedef enum
{
	eRunning = 0,/* A task is querying the state of itself, so must be running. */
	eReady,		/* The task being queried is in a read or pending ready list. */
	eBlocked,	/* The task being queried is in the Blocked state. */
	eSuspended,	/* The task being queried is in the Suspended state*/
	eDeleted,	/* The task being queried has been deleted, but its TCB has not yet been freed. */
	eInvalid	/* Used as an 'invalid state' value. */
} eTaskState;

4 任务优先级

FreeRTOS的最低优先级是0,分配给了空闲任务,空闲任务的优先级使用宏定义tskIDLE_PRIORITY进行表示,最大的优先级为需要用户进行配置;在FreeRTOS.h中可以看到预编译指令,具体如下;

#ifndef configMAX_PRIORITIES
	#error Missing definition:  configMAX_PRIORITIES must be defined in FreeRTOSConfig.h.  \
	See the Configuration section of the FreeRTOS API documentation for details.
#endif

#if configMAX_PRIORITIES < 1
	#error configMAX_PRIORITIES must be defined to be greater than or equal to 1.
#endif

configMAX_PRIORITIES需要用户在FreeRTOSConfig.h进行定义,则任务最大的优先级为configMAX_PRIORITIES - 1

5 相关函数

任务管理的函数声明和一些基本类型都封装在源码tasks.h中;

5.1 创建任务

使用xTaskCreate创建一个任务,具体如下所示;

// Task to be created.
 void vTaskCode( void * pvParameters )
 {
	 for( ;; )
	 {
		 // Task code goes here.
	 }
 }
 // Function that creates a task.
 void vOtherFunction( void )
 {
 static uint8_t ucParameterToPass;
 TaskHandle_t xHandle = NULL;

	 // Create the task, storing the handle.  Note that the passed parameter ucParameterToPass
	 // must exist for the lifetime of the task, so in this case is declared static.  If it was just an
	 // an automatic stack variable it might no longer exist, or at least have been corrupted, by the time
	 // the new task attempts to access it.
	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, &ucParameterToPass, tskIDLE_PRIORITY, &xHandle );
     configASSERT( xHandle );

	 // Use the handle to delete the task.
     if( xHandle != NULL )
     {
	     vTaskDelete( xHandle );
     }
 }

5.2 函数删除

使用函数vTaskDelete对函数进行删除;

 void vTaskDelete( TaskHandle_t xTaskToDelete ) PRIVILEGED_FUNCTION;

 void vOtherFunction( void )
 {
 TaskHandle_t xHandle;

	 // Create the task, storing the handle.
	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );

	 // Use the handle to delete the task.
	 vTaskDelete( xHandle );
 }

5.3 堵塞任务

使用vTaskDelay函数可以将任务堵塞一定时间;

 void vTaskDelay( const TickType_t xTicksToDelay ) PRIVILEGED_FUNCTION;
 void vTaskFunction( void * pvParameters )
 {
 // Block for 500ms.
 const TickType_t xDelay = 500 / portTICK_PERIOD_MS;

	 for( ;; )
	 {
		 // Simply toggle the LED every 500ms, blocking between each toggle.
		 vToggleLED();
		 vTaskDelay( xDelay );
	 }
 }

5.4 挂起和恢复

使用函数vTaskSuspend可以将函数挂起,通过vTaskResume(xHandle)函数可以将挂起的函数恢复到就绪状态;

 void vTaskSuspend( TaskHandle_t xTaskToSuspend ) PRIVILEGED_FUNCTION;

 void vAFunction( void )
 {
 	 TaskHandle_t xHandle;

	 // Create a task, storing the handle.
	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );

	 // ...

	 // Use the handle to suspend the created task.
	 vTaskSuspend( xHandle );

	 // ...

	 // The created task will not run during this period, unless
	 // another task calls vTaskResume( xHandle ).

	 //...


	 // Suspend ourselves.
	 vTaskSuspend( NULL );

	 // We cannot get here unless another task calls vTaskResume
	 // with our handle as the parameter.
 }

6 总结

先了解任务之前的状态,先学会使用FreeRTOS的常用接口,后续对于其调度算法和内存管理的算法可以分析和学习一下。


文中难免有错误和纰漏之处,请大佬们不吝赐教
创作不易,如果本文帮到了您;
请帮忙点个赞 👍👍👍;
请帮忙点个赞 👍👍👍;
请帮忙点个赞 👍👍👍;

posted @ 2020-04-08 23:09  小麦大叔  阅读(1192)  评论(0编辑  收藏  举报