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;
/* 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 )
{
/* 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 )
{
/* 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 )
{
/* 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 */
if( pxNewTCB != NULL )
{
#if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e9029 !e731 Macro has been consolidated for readability reasons. */
{
/* Tasks can be created statically or dynamically, so note this
* task was created dynamically in case it is later deleted. */
pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;
}
#endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */
prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
prvAddNewTaskToReadyList( pxNewTCB );
xReturn = pdPASS;
}
else
{
xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
}
return xReturn;
}
总结一下步骤:
- 申请堆栈内存(返回首地址)
- 申请任务控制块内存(返回首地址)
- 把前面申请的堆栈地址,赋值给控制块的堆栈成员
- 调用prvInitialiseNewTask初始化任务控制块中的成员
- 初始化堆栈为0xa5(可选)
- 记录栈顶,保存在pxTopOfStack
- 保存任务名字到pxNewTCB->pcTaskName[x]中
- 保存任务优先级到pxNewTCB->uxPriority
- 设置状态列表项的所属控制块,设置时间列表项的值
- 列表项的插入是从小到大插入,所以这里将越高优先级的任务他的事件列表项值设置越小,这样就可以排到前面
- 将任务句柄等于任务控制块
- 调用prvAddNewTaskToReadyList添加新创建任务到就绪列表中
- 记录任务数量uxCurrentNumberOfTasks++
- 判断新创建的任务是否为第一个任务
- 如果创建的是第一个任务,初始化任务列表prvInitialiseTaskLists
- 如果创建的不是第一个任务,并且调度器还未开始启动,比较新任务与正在执行的任务
优先级大小,新任务优先级大的话,将当前控制块重新指向新的控制块
- 将新的任务控制块添加到就绪列表中,使用函数prvAddTaskToReadyList
- 将uxTopReadyPriority相应bit置一,表示相应优先级有就绪任务,比如任务优先级为5,就将该变量的位5置一,方便后续任务切换判断,对应的就绪列表是否有任务存在
- 将新创建的任务插入对应的就绪列表末尾
- 如果调度器已经开始运行,并且新任务的优先级更大的话,进行一次任务切换
2. 任务删除的实现(了解)
任务删除的源码实现过程
void vTaskDelete( TaskHandle_t xTaskToDelete )
{
TCB_t * pxTCB;
taskENTER_CRITICAL();
{
/* 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 );
/* Reset the next expected unblock time in case it referred to
* the task that has just been deleted. */
prvResetNextTaskUnblockTime();
}
}
taskEXIT_CRITICAL();
/* If the task is not deleting itself, call prvDeleteTCB from outside of
* critical section. If a task deletes itself, prvDeleteTCB is called
* from prvCheckTasksWaitingTermination which is called from Idle task. */
if( pxTCB != pxCurrentTCB )
{
prvDeleteTCB( pxTCB );
}
/* Force a reschedule if it is the currently running task that has just
* been deleted. */
if( xSchedulerRunning != pdFALSE )
{
if( pxTCB == pxCurrentTCB )
{
configASSERT( uxSchedulerSuspended == 0 );
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
总结一下步骤:
- 获取所要删除任务的控制块:通过传入的任务句柄,判断所需要删除哪个任务,NULL代表删除自身
- 将被删除任务,移除所在列表:将该任务在所在列表中移除,包括:就绪、阻塞、挂起、事件等列表
- 判断所需要删除的任务
- 删除任务自身,需先添加到等待删除列表,内存释放将在空闲任务执行
- 删除其他任务,当前任务数量–:更新下一个任务的阻塞超时时间,以防被删除的任务就是下一个阻塞超时任务。
- 删除的任务为其他任务则直接释放内存prvDeleteTCB()
- 调度器正在运行且删除任务自身,则需要进行一次任务切换
3. 静态创建任务实现(了解)
静态创建任务不常用,其内部实现步骤可总结为:
- 获取控制块内存(首地址)
- 获取堆栈内存(首地址)
- 标记使用的静态的方式申请的TCB和堆栈内存
- 调用prvInitialiseNewTask 初始化任务块,并将控制块信息返回给任务句柄,以便后续返回句柄信息
- 调用prvAddNewTaskToReadyList 添加新创建任务到就绪列表中