【FreeRTOS】FreeRTOS 源码学习笔记 (4) 任务创建xTaskCreate + 常用结构体TCB、xLIST

1. 引言

经过第一节的移植,我们已经拿到了一个可以用的工程。 经过第二三节的基础知识,我们对基本的数据结构,列表、队列这些也有了一个了解。
接下来就可以单步跟踪了,看一下系统是怎么运行的。
使用FreeRTOS,首先要新建一个任务,我们可以单步跟一下它的运行流程,在调试之前,还是要先把最重要的几个结构体整理一下。

(本文初版为2020.4.12,FreeRTOS的代码版本为FreeRTOS Kernel V10.3.1)

2. 重要结构体

2.1 TCB

做FreeRTOS肯定首先要把大名鼎鼎的TCB干掉。
直接上源码先

//task.c 
//line 250

/*
 * Task control block.  A task control block (TCB) is allocated for each task,
 * and stores task state information, including a pointer to the task's context
 * (the task's run time environment, including register values)
 */
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. */

	#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 )
		/* Allocate a Newlib reent structure that is specific to this task.
		Note Newlib support has been included by popular demand, but is not
		used by the FreeRTOS maintainers themselves.  FreeRTOS is not
		responsible for resulting newlib operation.  User must be familiar with
		newlib and must provide system-wide implementations of the necessary
		stubs. Be warned that (at the time of writing) the current newlib design
		implements a system-wide malloc() that must be provided with locks.

		See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html
		for additional information. */
		struct	_reent xNewLib_reent;
	#endif

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

	/* See the comments in FreeRTOS.h with the definition of
	tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */
	#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 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

	#if( configUSE_POSIX_ERRNO == 1 )
		int iTaskErrno;
	#endif

} tskTCB;

/* The old tskTCB name is maintained above then typedefed to the new TCB_t name
below to enable the use of older kernel aware debuggers. */
typedef tskTCB TCB_t;

一些可选功能先跳过,抽出其中几个关键的member说一下。

{
	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. */
  • pxStack : 每个任务都有一个栈帧空间,pxStack 指的就是这个空间的起始地址(低地址)。(malloc开出来的地址)
  • pxTopOfStack:栈顶的位置。随着入栈出栈操作,栈顶会一直变。
  • pxEndOfStack:栈空间的结尾,如果栈向上生长,就是栈帧空间最大的地址,如果栈向下,就是栈帧空间最小的地址,也就是pxStack,所以portSTACK_GROWTH > 0时才会有这个member,不然重复。
  • xStateListItem:状态列表,表明当前系统的状态。挂在链表中是方便来找,如果是用一个变量来表明,每次还得遍历一遍来找,效率低,链表方便。
  • xEventListItem:事件列表
  • uxPriority : 优先级。FreeRTOS的优先级是越大越优先。最大值是在头文件里配置的,配置一个合适的值就好,不然太大也浪费。因为每个优先级其实也会申请空间来存列表。

2.2 xList

列表的结构体在 列表 里介绍了。
使用起来基本如下图:

网上找了一张图,侵删。
在这里插入图片描述

有些列表是有序的。要用vListInsert插入,按照value值组成一个升序,在有序列表中,xListEnd.pxNext指向的是头,xListEnd.pxPrevious指向的是尾巴。在真实使用时,要根据顺序来做判断的。
有些链表是无序的。vListInsertEnd插入,只是表示一个集合。无序列表中,具体到FreeRTOS中的轮询列表,它不标记逻辑头尾,因为vListInsertEnd()插入时并不是根据xListEnd,而是pxIndex

3. 任务创建流程

任务创建主要使用xTaskCreate接口来创建,流程十分简介。

  1. 开空间
    任务需要管理 和 运行,任务管理需要TCB控制块,任务运行需要任务堆栈。
    所以xTaskCreate的开头就是为这两部分需求开空间。在具体代码中会根据栈的生长方向来做一个malloc顺序的调整,主要就是为了如果栈溢出,不至于覆盖到TCB结构体。这里也是比较细节的。
    pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );负责给TCB模块开空间。
    pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );负责给堆栈开空间,这个开空间的控制参数usStackDepth 是从函数入参传进来的。
  2. TCB初始化
    接下来,就是对TCB结构体的参数一堆初始化,调用prvInitialiseNewTask接口来进行。这里面主要是对TCB结构体成员进行初始化,包括栈空间地址pxTopOfStack的赋值,任务名pcTaskName的赋值等。
  • pxTopOfStack,对pxTopOfStack赋值是根据栈生长方向来计算。根据栈空间的起始地址(malloc开的低地址)和整个栈的大小,来判断pxTopOfStack在栈空间最低还是最高位置(另外做一下字节对齐)
  • pcTaskName,这里通过对每个字符的赋值来填充pcTaskName,在最后添加’\0’结束符。而没有用strcpy这种,减少对库的依赖。
  • uxPriority,优先级直接用配置值
  • xStateListItem,使用vListInitialiseItem接口初始化状态列表。
  • xEventListItem,使用vListInitialiseItem接口初始化事件列表。
    然后初始化栈,栈第一次是空的,虽然是第一次用,但是要把栈改成一种“从任务切换出来”的样子,这样下次任务切换回来的时候,就不用针对第一次使用这个任务的栈做处理了。
  1. 加入就绪列表
    最后把任务先加入到ready列表中(prvAddNewTaskToReadyList),等待调度。

基本流程图如下
在这里插入图片描述

4. 参考链接

发布了25 篇原创文章 · 获赞 7 · 访问量 5099

猜你喜欢

转载自blog.csdn.net/tao475824827/article/details/105478612
今日推荐