任务控制块
任务控制块中主要保存着任务的优先级、任务名以及任务函数的栈起始地址等信息,经过裁剪后的官方源码如下所示:
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. */
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;
任务创建函数
以动态创建任务为例,创建函数如下:
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 );
通过分析官方源码可知,任务创建函数的主要内容包括:
- 分配任务函数所需的栈;
- 分配任务控制块的内存;
- 初始化任务控制块的内容;
- 将pxTaskCode和pvParameters写入已经分配好的栈中;(不保存在任务控制块中)
- 将任务添加进就绪态的链表中。
相关代码经过裁剪后如下所示:
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
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;
StackType_t * pxStack;
// 分配任务所需要的栈
pxStack = pvPortMallocStack( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );
// 分配TCB所需的内存
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
// 初始化TCB内容并初始化栈
prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
// 将任务写入就绪态链表
prvAddNewTaskToReadyList( pxNewTCB );
}
#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
// prvInitialiseNewTask函数会将pxTaskCode和pvParameters写入栈中
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters, xRunPrivileged );
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. */
pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */
*pxTopOfStack = portINITIAL_XPSR; /* xPSR */
pxTopOfStack--;
*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; /* PC */
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) prvTaskExitError; /* LR */
pxTopOfStack -= 5; /* R12, R3, R2 and R1. */
*pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */
pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */
return pxTopOfStack;
}
根据以上代码可知,在执行任务时,pxTaskCode将被写入PC寄存器中,pvParameters被写入R0寄存器中。任务创建函数执行完后,任务所需的栈示意图如下。