FreeRTOS任务创建和删除(动态)内部实现过程
5ff56398-744b-4366-8c7e-80f88877231b
往深处去看代码花了好久才理解的差不多,特此记录总结下,
下面将详细探究下FreeRTOS中任务创建和删除(动态)内部实现的过程,参考正点原子B站视频:第11讲 动态任务创建和删除详细过程(函数解析)_哔哩哔哩_bilibili
具体来分析代码,首先来看这个函数:
这个函数也就是动态创建任务的函数,入口参数具体为下图这些值:
函数名称: xTaskCreate,返回类型: BaseType_t,通常是一个整数类型,用于表示函数执行的成功与否。在FreeRTOS中,成功创建任务通常返回pdPASS(值为1),失败则返回pdFAIL(值为0)。
从keil里面进入这个函数里面:
TCB结构体详解
首先可以上面这些,那么来一句一句去看下这个代码作用。首先TCB_t * pxNewTCB
创建了一个指向任务控制块(Task Control Block,TCB)的指针。TCB是FreeRTOS用来管理任务的结构体。我们一步一步往里面找个结构体的定义:代码如下:
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 )
configRUN_TIME_COUNTER_TYPE ulRunTimeCounter; /*< Stores the amount of time the task has spent in the Running state. */
#endif
#if ( ( configUSE_NEWLIB_REENTRANT == 1 ) || ( configUSE_C_RUNTIME_TLS_SUPPORT == 1 ) )
configTLS_BLOCK_TYPE xTLSBlock; /*< Memory block used as Thread Local Storage (TLS) Block for the task. */
#endif
#if ( configUSE_TASK_NOTIFICATIONS == 1 )
volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
#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;
volatile StackType_t * pxTopOfStack;
指向任务堆栈顶部的指针。这是最后一个被放置在任务堆栈上的元素的位置。在任务切换时,这个位置用于保存或恢复任务的上下文。
xMPU_SETTINGS xMPUSettings;(条件编译)
如果启用了内存保护单元(MPU)包装器(portUSING_MPU_WRAPPERS == 1),这个成员用于存储MPU的配置设置。这有助于实现任务级的内存保护。
ListItem_t xStateListItem;
用于将任务链接到不同的任务列表中,这些列表表示任务的状态(就绪、阻塞、挂起等)。
ListItem_t xEventListItem;
当任务因为等待某个事件(如信号量、消息队列等)而被阻塞时,这个成员用于将任务链接到相应的事件列表中。
UBaseType_t uxPriority;
任务的优先级。在FreeRTOS中,数字越大表示优先级越高。
StackType_t * pxStack;
指向任务堆栈开始的指针。堆栈是任务执行时用于存储局部变量、函数参数等的内存区域。
char pcTaskName[configMAX_TASK_NAME_LEN];
任务的名称,仅用于调试目的。这是一个字符数组,长度由configMAX_TASK_NAME_LEN宏定义。
正点原子也对其进行了总结,具体如下图:
定义了任务创建函数返回值类型。
BaseType_t xTaskCreate()
接下来分析这一部分代码:
#if ( portSTACK_GROWTH > 0 )
{
/* Allocate space for the TCB. Where the memory comes from depends on
* the implementation of the port malloc function and whether or not static
* allocation is being used. */
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
if( pxNewTCB != NULL )
{
memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) );
/* Allocate space for the stack used by the task being created.
* The base of the stack memory stored in the TCB so the task can
* be deleted later if required. */
pxNewTCB->pxStack = ( StackType_t * ) pvPortMallocStack( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
if( pxNewTCB->pxStack == NULL )
{
/* Could not allocate the stack. Delete the allocated TCB. */
vPortFree( pxNewTCB );
pxNewTCB = NULL;
}
}
}
#else /* portSTACK_GROWTH */
{
StackType_t * pxStack;
/* Allocate space for the stack used by the task being created. */
pxStack = pvPortMallocStack( ( ( ( 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 )
{
memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) );
/* Store the stack location in the TCB. */
pxNewTCB->pxStack = pxStack;
}
else
{
/* The stack cannot be used as the TCB was not created. Free
* it again. */
vPortFreeStack( pxStack );
}
}
else
{
pxNewTCB = NULL;
}
}
#endif /* portSTACK_GROWTH */
它处理任务控制块(TCB)和任务堆栈的分配。代码的行为根据portSTACK_GROWTH的定义而变化,这个宏定义指示堆栈是向上增长(正值)还是向下增长(负值)。
若是向上增长:
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
上面这句代码sizeof(TCB_t)
:这部分计算TCB_t结构体的大小(以字节为单位)。TCB_t是FreeRTOS中定义的一个结构体类型,它包含了任务运行所需的所有状态信息,比如任务堆栈的指针、任务优先级、任务状态等。使用sizeof运算符可以确保为TCB分配足够的内存来存储这个结构体的所有成员。pvPortMalloc(sizeof(TCB_t)):
pvPortMalloc是一个内存分配函数,它根据FreeRTOS的移植(port)层定义。这个函数的行为类似于标准C库中的malloc函数,但它可能会包含一些特定于RTOS或硬件平台的优化或特性。该函数接受一个参数,即需要分配的内存大小(字节为单位),并返回一个void类型的指针,指向分配的内存。如果分配失败,它将返回NULL。在这里,pvPortMalloc被用来请求分配足够的内存以存储一个TCB_t结构体。(TCB_t *)pvPortMalloc(sizeof(TCB_t)):
由于pvPortMalloc返回一个void类型的指针,而在C语言中,void指针可以指向任何类型的数据,但使用时通常需要转换为具体类型的指针。这里将void类型的返回值转换为TCB_t*类型,这样就可以将这个指针赋值给pxNewTCB变量,后者是一个指向TCB_t类型的指针。这种类型转换是必要的,因为它告诉编译器这块内存将被用作TCB_t类型的对象。这样,当通过pxNewTCB访问TCB的成员时,编译器就能正确地解引用指针并按照TCB_t结构体的布局来访问内存。
if( pxNewTCB != NULL )
{
memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) );
/* Allocate space for the stack used by the task being created.
* The base of the stack memory stored in the TCB so the task can
* be deleted later if required. */
pxNewTCB->pxStack = ( StackType_t * ) pvPortMallocStack( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
if( pxNewTCB->pxStack == NULL )
{
/* Could not allocate the stack. Delete the allocated TCB. */
vPortFree( pxNewTCB );
pxNewTCB = NULL;
}
}
这行代码的作用是将新分配的任务控制块(Task Control Block, TCB)内存区域中的所有字节设置为0。memset函数是一个在C语言中广泛使用的标准库函数,用于将一块内存中的所有字节都设置为特定的值。在这个特定的场景中,memset被用来初始化或清零pxNewTCB指向的内存区域。这部分是将pxNewTCB指针转换为void类型。pxNewTCB是一个指向TCB_t类型的指针,而memset函数接受一个void类型的指针作为其第一个参数,这意味着它可以接受任何类型的指针。void*类型的指针是一个通用指针类型。这行代码的作用是为新创建的任务分配堆栈空间。在FreeRTOS和许多其他操作系统中,每个任务都需要有自己的堆栈(stack),这是因为堆栈用于存储函数调用时的局部变量、返回地址以及CPU寄存器等状态信息,是任务执行过程中不可或缺的部分。具体来说,这行代码执行了以下几个步骤:
- 计算所需堆栈空间的大小:
-
((size_t) usStackDepth) * sizeof(StackType_t)
这部分计算了所需堆栈空间的总字节数。usStackDepth
表示堆栈的深度,即堆栈可以容纳的元素数量(通常是指可以存储的最大CPU寄存器或变量的数量)。sizeof(StackType_t)
是堆栈中每个元素的大小(字节)。因此,整个表达式计算出整个堆栈所需的字节数。
- 调用
pvPortMallocStack
分配堆栈空间:
-
pvPortMallocStack(((size_t) usStackDepth) * sizeof(StackType_t))
这部分调用了pvPortMallocStack
函数,尝试分配计算出的字节数。pvPortMallocStack
是一个专为堆栈分配设计的内存分配函数,可能会有针对特定架构或需求的优化。如果成功,它将返回指向分配的堆栈空间的起始地址的指针;如果失败,它将返回NULL
。
- 将堆栈空间的起始地址存储在TCB中:
-
(StackType_t *)
这部分是一个类型转换,将pvPortMallocStack
函数返回的指针转换为StackType_t *
类型。StackType_t
通常是针对特定平台定义的一个类型,以确保堆栈的对齐和大小适合于该平台的CPU架构。 -
pxNewTCB->pxStack =
这部分将分配的堆栈空间的起始地址存储在任务控制块(TCB)的pxStack
成员变量中。这样,操作系统就能够在任务切换时保存和恢复任务的堆栈状态。
if( pxNewTCB->pxStack == NULL )
{
/* Could not allocate the stack. Delete the allocated TCB. */
vPortFree( pxNewTCB );
pxNewTCB = NULL;
}
- 条件判断:
-
if( pxNewTCB->pxStack == NULL )
这一行是一个条件判断,检查新任务的任务控制块(TCB)中的堆栈指针(pxStack
)是否为NULL
。在这里,pxNewTCB
是一个指向新任务的 TCB 的指针。pxStack
是 TCB 结构体中的一个成员,指向任务的堆栈空间的起始地址。如果pxStack
为NULL
,这意味着之前尝试为新任务分配堆栈空间失败了(可能是因为系统内存不足)。
- 释放已分配的 TCB:
-
vPortFree( pxNewTCB );
如果堆栈空间分配失败,这行代码将执行。vPortFree
是一个内存释放函数,用于释放之前通过某种内存分配函数(如pvPortMalloc
)分配的内存。这里,它被用来释放之前为新任务分配的 TCB 的内存。这是必要的,因为如果我们不能为任务分配必要的堆栈空间,那么保留这个部分初始化的 TCB 是没有意义的,也可能导致内存泄漏。
- 将 TCB 指针设为 NULL:
pxNewTCB = NULL;
这行代码将pxNewTCB
指针设为NULL
,确保之后的代码不会意外地使用到这个已经被释放的内存地址。设置为NULL
是一个好习惯,可以帮助防止野指针错误。野指针是指向未知内存地址的指针,尝试访问野指针可能导致不可预测的行为,包括程序崩溃。
上面这幅图来自正点原子的思维导图,我们前面的程序主要干了2件事情,申请堆栈内存和申请任务控制块内存,以及将申请的堆栈内存地址赋值给控制块的堆栈成员。加下来就是调用prvInitialiseNewTask这个函数去初始化任务控制块中的成员。
prvInitialiseNewTask
函数
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 )
首先上面是该函数的入口参数,
函数 prvInitialiseNewTask
是 FreeRTOS 中用于初始化新任务的静态函数。这个函数不直接由用户代码调用,而是由 FreeRTOS 的任务创建API(如xTaskCreate
或xTaskCreateStatic
)内部调用。它负责设置新任务的各种属性和准备任务的执行环境。下面是对该函数参数的解释:
参数解释
- TaskFunction_t pxTaskCode:
- 这是一个指向任务函数的指针。任务函数是当任务启动时执行的函数,其类型
TaskFunction_t
通常定义为返回void
且接受一个void *
参数的函数指针。
- const char * const pcName:
- 这是指向任务名称的指针。任务名称是一个字符串,用于标识任务,主要用于调试目的。
- const uint32_t ulStackDepth:
- 这表示任务栈的深度,即任务栈可以容纳的最大元素数量。栈深度的单位通常是字或者栈元素的数量,具体取决于平台。
- void * const pvParameters:
- 这是传递给任务函数的参数。它是一个
void *
类型,可以指向任何类型的数据,提供了一种灵活的方式将数据从任务创建者传递给任务函数。
- UBaseType_t uxPriority:
- 这是任务的优先级。在FreeRTOS中,较高的数值表示较高的优先级。任务优先级决定了任务在何时被调度执行。
- TaskHandle_t * const pxCreatedTask:
- 这是一个指向任务句柄的指针。任务句柄用于在任务创建后引用该任务。如果创建成功,这个指针会被设置为指向任务控制块(TCB)的指针。
- TCB_t * pxNewTCB:
- 这是指向新任务的任务控制块(TCB)的指针。TCB是一个结构体,包含了管理任务所需的所有信息,如任务栈的指针、任务状态、优先级等。
- const MemoryRegion_t * const xRegions:
- 这是指向内存区域定义数组的指针。这个参数允许为任务指定特定的内存区域,这些区域可以用于任务的变量或数据存储。这是一种高级功能,通常用于需要精细控制内存使用的应用。
在 FreeRTOS 中,StackType_t * pxTopOfStack;
和 UBaseType_t x;
这两行代码通常出现在任务的初始化或创建过程中。它们各自的作用如下:
1:StackType_t * pxTopOfStack;
- 作用:这是一个指向栈顶的指针。在任务创建时,FreeRTOS 为每个任务分配一个独立的栈空间,用于存储局部变量、函数参数、返回地址等信息。
pxTopOfStack
指向这个栈的顶部位置,即最后一个被写入的位置。在不同的架构中,栈的增长方向可能不同(向上增长或向下增长),因此“栈顶”的概念也相应地有所变化。在任务切换时,FreeRTOS 会保存当前任务的pxTopOfStack
值,以便之后能够恢复到正确的位置继续执行。 - 初始化:在任务创建过程中,
pxTopOfStack
会被初始化为指向分配给该任务的栈空间的某个位置。具体的初始化位置取决于栈的增长方向以及CPU架构的要求。 - 由于不使用MPU,所以上图代码这部分可以跳过。
#if ( tskSET_NEW_STACKS_TO_KNOWN_VALUE == 1 )
{
/* Fill the stack with a known value to assist debugging. */
( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) );
}
这段代码是在一个条件编译块中,用于在任务的堆栈空间被分配后,用一个已知的值填充整个堆栈。这是一种在开发过程中常用的调试技巧,它有助于开发者在调试时识别堆栈的使用情况,尤其是用于检测堆栈溢出或未初始化的堆栈访问。下面是这段代码的详细解释
堆栈初始化
{
/* Fill the stack with a known value to assist debugging. */
( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) );
}
- 注释:首先,代码中的注释说明了这个代码块的目的——“用一个已知的值填充堆栈以辅助调试”。
- memset函数调用:
memset
函数用于将一段内存区域全部填充为指定的值。这里,它被用来填充新任务的堆栈空间。
-
pxNewTCB->pxStack
是一个指向新任务堆栈空间起始地址的指针。 -
( int ) tskSTACK_FILL_BYTE
指定了用于填充堆栈的值。tskSTACK_FILL_BYTE
是一个预定义的宏,代表一个特定的字节值。通过将堆栈填充为这个值,开发者可以在调试时更容易地识别堆栈的使用边界和溢出情况。 -
( size_t ) ulStackDepth * sizeof( StackType_t )
计算了整个堆栈空间的大小(以字节为单位)。ulStackDepth
是堆栈的深度(即可以容纳的元素数量),sizeof(StackType_t)
是堆栈中每个元素的大小。这个表达式的结果是需要填充的总字节数。
- 类型转换:代码中使用了类型转换,确保传递给
memset
的参数类型正确。特别是,将tskSTACK_FILL_BYTE
转换为(int)
,确保它以正确的类型传递给memset
。
接下来看这一部分代码:
#if ( portSTACK_GROWTH < 0 )
{
pxTopOfStack = &( pxNewTCB->pxStack[ ulStackDepth - ( uint32_t ) 1 ] );
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 ) );
#if ( configRECORD_STACK_HIGH_ADDRESS == 1 )
{
/* Also record the stack's high address, which may assist
* debugging. */
pxNewTCB->pxEndOfStack = pxTopOfStack;
}
#endif /* configRECORD_STACK_HIGH_ADDRESS */
}
#else /* portSTACK_GROWTH */
{
pxTopOfStack = pxNewTCB->pxStack;
/* Check the alignment of the stack buffer is correct. */
configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxNewTCB->pxStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );
/* The other extreme of the stack space is required if stack checking is
* performed. */
pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
}
#endif /* portSTACK_GROWTH */
这里先要了解俩种增长方式究竟是啥:
了解了之后就知道为啥这样计算地址了。pxTopOfStack = &( pxNewTCB->pxStack[ ulStackDepth - ( uint32_t ) 1 ] );
这个对应向下增加,所以栈顶地址应该位于堆栈最后一个元素所在的位置,所以用ulStackDepth - 1得到被设置为指向pxStack数组的最后一个元素的地址,这是因为在向下增长的栈中,栈顶是在栈数组的末端。向上增长的栈,栈顶指针在第一个元素的位置。
if( pcName != NULL )//这部分代码主要作用是任务名称保存到TCB结构体成员pctaskname上去。
{
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';
}
configASSERT( uxPriority < configMAX_PRIORITIES );/*这段代码来
自于FreeRTOS的任务创建或优先级设置函数中,它的主要作用是确保给定的任务优先级uxPriority在有效范围内。*/
if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
{
uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
pxNewTCB->uxPriority = uxPriority;
#if ( configUSE_MUTEXES == 1 )
{
pxNewTCB->uxBasePriority = uxPriority;
}
#endif /* configUSE_MUTEXES */
vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
vListInitialiseItem( &( pxNewTCB->xEventListItem ) );
/*这两行代码分别初始化任务的状态列表项xStateListItem和事件列表项xEventListItem。
在FreeRTOS中,列表项(ListItem_t)是用于将多个对象(如任务)链接在一起形成列表的结构。
这里的初始化操作通常包括设置列表项的各种成员到一个合理的初始状态,例如将其链接指针(
用于指向列表中的下一个和上一个元素)设置为指向自己,表示该列表项尚未链接到任何列表中。*/
/* 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 );
/*这两行代码为xStateListItem和xEventListItem设置所有者,即指向包含这些列表项的TCB的指针。这使得
当我们有一个指向列表项的指针时,可以轻松地找到它所属的TCB。在FreeRTOS的列表处理函数中,
这种反向查找机制经常被用来从列表中的项访问到完整的任务控制块。*/
/* Event lists are always in priority order. */
listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority );
/*这行代码为xEventListItem设置了一个值,该值基于任务的优先级计算得出。这里的计算方式是configMAX_PRIORITIES - uxPriority,意味着优先级较高的任务会得到一个较小的值。
在FreeRTOS中,事件列表(如延时列表、就绪列表)往往根据这个值来排序,
以确保优先级较高的任务能够先于优先级较低的任务被选中执行。
这种设计允许FreeRTOS的调度器以优先级为基准快速选择下一个要运行的任务。 */
listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB );
#if ( portHAS_STACK_OVERFLOW_CHECKING == 1 )
{
#if ( portSTACK_GROWTH < 0 )
{
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters );
}
#else /* portSTACK_GROWTH */
{
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters );
}
#endif /* portSTACK_GROWTH */
}
#else /* portHAS_STACK_OVERFLOW_CHECKING */
{
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
}
#endif /* portHAS_STACK_OVERFLOW_CHECKING */
我们主要看这一句代码:
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters );
该函数具体内容为以下部分:
参照正点原子中视频具体要往堆栈存的值就是这些。pxNewTCB->pxTopOfStack 更新后的位置就如上图所示,具体这个函数要做的事情参考正点原子课件的思维导图可以得到:
这个函数里面要做的事情就是上面这些。
prvAddNewTaskToReadyList( pxNewTCB );
接下来来理解这个函数:
正点原子总结的要做的事情如上图所示:
具体去看代码暂时还没理解很清楚。等到日后理解了填下坑。