目录
任务创建和删除API函数
动态创建任务函数原形
实现动态创建任务流程
任务控制块结构体成员介绍
静态创建任务
任务删除函数
删除任务流程
BaseType_t xTaskCreate创建任务函数内部实现
prvInitialiseNewTask初始化任务控制块函数内部实现
prvAddNewTaskToReadyList加入就绪列表函数内部实现
vTaskDelete删除任务函数内部实现
动态创建任务:任务的任务控制块以及任务的栈空间所需的内存,均由FreeRTOS从 FreeRTOS 管理的堆中分配。
静态创建任务:任务的任务控制块以及任务的栈空间所需的内存,需用户分配提供。
BaseType txTaskCreate
{
TaskFunction_t pxTaskCode, /*指向任务函数的指针*/
const char * const pcName, /*任务名字,最大长度configMAX TASK NAMELEN*/
const configSTACK_DEPTH_TYPE usStackDepth, /*任务堆栈大小,注意字为单位*/
void * const pvParameters,/*传递给任务函数的参数一般没用到*/
UBaseType_t uxPriority, /*任务优先级,范围:0~configMAX_PRIORITIES-1*/
TaskHandle t * const pxCreatedTask, /*任务句柄,就是任务的任务控制块*/
}
1.将宏configSUPPORT_DYNAMIC_ALLOCATION配置为1
2.定义函数入口参数
3.编写任务函数
此函数创建的任务会立刻进入就绪态,由任务调度器调度运行
4.申请推栈内存&任务控制快内存
5.TCB任务控制块结构体成员赋值
6.添加新任务到就绪列表中
typedef struct tskTaskControlBlock
{
volatile StackType t* pxTopOfStack; /*任务栈栈顶,必须为TCB的第一个成员*/
Listltem t xStateListltem; /*任务状态列表项*/
Listltem_t xEventListltem; /*任务事件列表项*/
UBaseType_t tuxPriority; /*任务优先级,数值越大,优先级越大*/
StackType_t* pxStack; /*任务栈起始地址*/
Char pcTaskName[configMAX_TASK_NAME_LEN];/*任务名字*/
//........
//省略很多条件编译的成员
}tskTCB;
任务栈栈顶,在任务切换时的任务上下文保存、任务恢复息息相关,每个任务都有属于自己的任务控制块,类似身份证。
1.需将宏configSUPPORT_STATIC_ALLOCATION 配置为1
2.定义空闲任务&软件定时器任务的任务堆栈及TCB
3.实现两个接口函数,空闲任务内存赋值vApplicationGetldleTaskMemory( )
软件定时器内存赋值vApplicationGetTimerTaskMemory ()
4.定义函数入口参数
5.编写任务函数
此函数创建的任务会立刻进入就绪态,由任务调度器调度运行
6.TCB结构体成员赋值
7.添加新任务到就绪列表中
void vTaskDelete (TaskHandle t xTaskToDelete);
用于删除已被创建的任务,被删除的任务将从就绪态任务列表、阻塞态任务列表、挂起态任务列表和事件列表中移除。
注意:
1.当传入的参数为NULL,则代表删除任务自身(当前正在运行的任务)
2.(动态创建任务)空闲任务会负责释放被删除任务中由系统分配的内存,(静态创建任务)但是由用户在任务删除前申请的内存,则需要由用户在任务被删除前提前释放,否则将导致内存泄露
1.使用删除任务函数,需将宏INCLUDE_vTaskDelete 配置为1
2.入口参数输入需要删除的任务句柄(NULL代表删除本身)
3.获取所要删除任务的控制块,通过传入的任务句柄,判断所需要删除哪个任务,NULL代表删除自身。
4.将被删除任务,移除所在列表,将该任务在所在列表中移除,包括:就绪、阻塞、挂起、事件等列表。
5.判断所需要删除的任务:1.删除任务自身,需先添加到等待删除列表,内存释放将在空闲任务执行。2.删除其他任务,释放内存,任务数量。
6.更新下一个任务的阻塞超时时间,以防被删除的任务就是下一个阻塞超时的任务
1.申请堆栈内存(返回首地址)
2.申请任务控制块内存(返回首地址)
3.把前面申请的堆栈地址,赋值给控制块的堆栈成员
4.调用prvinitialiseNewTask初始化任务控制块中的成员
5.调用prvAddNewTaskToReadyList添加新创建任务到就绪列表中
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint16_t usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
{
/*首先函数进来就创建了一个TCB_t 结构体指针,TCB结构体可以自己跳转过去
看下就是保存任务的一些特征。*/
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. */
/*portSTACK_GROWTH > 0这是判断栈的生长方向,小于0就是向下生长,
大于0就是向上生长,(注意推是和栈是相反的)。*/
#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 * ) pvPortMalloc( ( ( ( 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 定义一个变量存储任务堆栈的首地址*/
StackType_t *pxStack;
/* Allocate space for the stack used by the task being created. */
/*通过MallocStack函数申请堆大小(字为单位)*/
pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
/*判断是否申请成功*/
if( pxStack != NULL )
{
/* Allocate space for the TCB. */
/*如果申请堆栈成功,就申请TCB任务控制块*/
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e961 MISRA exception as the casts are only redundant for some paths. */
if( pxNewTCB != NULL )
{
/* Store the stack location in the TCB. */
/*申请TCB任务块成功,就把堆栈的首地址赋值给TCB里面存放首地址变量里*/
pxNewTCB->pxStack = pxStack;
}
else
{
/* The stack cannot be used as the TCB was not created. Free
it again. */
/*如果申请失败,就把申请的任务堆栈释放掉*/
vPortFree( pxStack );
}
}
else
{
/*申请失败就直接返回NULL*/
pxNewTCB = NULL;
}
}
#endif /* portSTACK_GROWTH */
/*判断TCB任务控制块是否成功*/
if( pxNewTCB != NULL )
{
#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
{
/* 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 /* configSUPPORT_STATIC_ALLOCATION */
/*初始化任务控制块*/
prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
/*加入到就绪列表中*/
prvAddNewTaskToReadyList( pxNewTCB );
xReturn = pdPASS;
}
else
{
xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
}
return xReturn;
}
1.初始化堆栈为0xa5 (可选)
2.己录栈顶,保存在pxTopOfStack
3.保存任务名字到pxNewTCB->pcTaskName[ ×]中
4.保存任务优先级到pxNewTCB->uxPriority
5.设置状态列表项的所属控制块,设置事件列表项的位
6.列表项的插入是从小到大插入,所以这里将越高优先级的任务的事件列表项值设置越小,这样就可以拍到前面
7.调用pxPortInitialiseStack初始化任务堆栈,用于保存当前任务上下文寄存器信息,已备后续任务切换使用
8.将任务句柄等于任务控制块
static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint32_t ulStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask,
TCB_t *pxNewTCB,
const MemoryRegion_t * const xRegions ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
{
StackType_t *pxTopOfStack;
UBaseType_t x;
#if( portUSING_MPU_WRAPPERS == 1 )
// /* Should the task be created in privileged mode? */
// BaseType_t xRunPrivileged;
// if( ( uxPriority & portPRIVILEGE_BIT ) != 0U )
// {
// xRunPrivileged = pdTRUE;
// }
// else
// {
// xRunPrivileged = pdFALSE;
// }
// uxPriority &= ~portPRIVILEGE_BIT;
#endif /* portUSING_MPU_WRAPPERS == 1 */
/* Avoid dependency on memset() if it is not required. */
#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) )
{
/* Fill the stack with a known value to assist debugging. */
/*跟着调试使用,对某一个起始地址开始一段内存赋值成某一个值,把任务堆栈全部赋值成tskSTACK_FILL_BYTE(A5),当不是A5的时候证明被用了,这样就能检查出还剩多少内存*/
( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) );
}
#endif /* ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) ) */
/* Calculate the top of stack address. This depends on whether the stack
grows from high memory to low (as per the 80x86) or vice versa.
portSTACK_GROWTH is used to make the result positive or negative as required
by the port. */
/*判断栈是不是先下生长的*/
#if( portSTACK_GROWTH < 0 )
{
/*栈是从低到高生长pxNewTCB->pxStack这里是栈的首地址, ulStackDepth是整个推栈的大小
pxNewTCB->pxStack首地址+整个推栈大小减1就是栈顶了,获取栈顶*/
pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
/*做8字节对齐*/
pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); /*lint !e923 MISRA exception. Avoiding casts between pointers and integers is not practical. Size differences accounted for using portPOINTER_SIZE_TYPE type. */
/* Check the alignment of the calculated top of stack is correct. */
/*断言*/
configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );
}
#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 */
/* Store the task name in the TCB. */
/*存储任务名字到TCB任务控制块中*/
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 ] == 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';
/* This is used as an array index so must ensure it's not too large. First
remove the privilege bit if one is present. */
/*判断任务优先级是不是最大*/
if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
{
/*当是等于最大优先级的时候,就最大优先级-1,32-1=31 优先级是0~31*/
uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/*赋值给TCB任务控制块优先级成员变量*/
pxNewTCB->uxPriority = uxPriority;
/*判断是否使用互斥量*/
#if ( configUSE_MUTEXES == 1 )
{
/*保存优先级到TCB任务控制块的基优先级变量里*/
pxNewTCB->uxBasePriority = uxPriority;
pxNewTCB->uxMutexesHeld = 0;
}
#endif /* configUSE_MUTEXES */
/*初始状态列表项和事假列表项*/
vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
vListInitialiseItem( &( pxNewTCB->xEventListItem ) );
/* 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 );
/* Event lists are always in priority order. */
/*设置事件列表项的数值,事件列表项数值越小,排在越前面,这里用最高优先级31-优先级,事件列表是数值越小排在最前面,所有优先级越大(数值越大)得会优先得到事件*/
listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
/*把事件列表项的归属者给任务控制块*/
listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB );
#if ( portCRITICAL_NESTING_IN_TCB == 1 )
{
// pxNewTCB->uxCriticalNesting = ( UBaseType_t ) 0U;
}
#endif /* portCRITICAL_NESTING_IN_TCB */
#if ( configUSE_APPLICATION_TASK_TAG == 1 )
{
// pxNewTCB->pxTaskTag = NULL;
}
#endif /* configUSE_APPLICATION_TASK_TAG */
#if ( configGENERATE_RUN_TIME_STATS == 1 )
{
// pxNewTCB->ulRunTimeCounter = 0UL;
}
#endif /* configGENERATE_RUN_TIME_STATS */
#if ( portUSING_MPU_WRAPPERS == 1 )
{
// vPortStoreTaskMPUSettings( &( pxNewTCB->xMPUSettings ), xRegions, pxNewTCB->pxStack, ulStackDepth );
}
#else
{
/* Avoid compiler warning about unreferenced parameter. */
( void ) xRegions;
}
#endif
#if( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 )
{
// for( x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ )
// {
// pxNewTCB->pvThreadLocalStoragePointers[ x ] = NULL;
}
}
#endif
#if ( configUSE_TASK_NOTIFICATIONS == 1 )
{
pxNewTCB->ulNotifiedValue = 0;
pxNewTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
}
#endif
#if ( configUSE_NEWLIB_REENTRANT == 1 )
{
// /* Initialise this task's Newlib reent structure. */
// _REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) );
}
#endif
#if( INCLUDE_xTaskAbortDelay == 1 )
{
// pxNewTCB->ucDelayAborted = pdFALSE;
}
#endif
/* Initialize the TCB stack to look as if the task was already running,
but had been interrupted by the scheduler. The return address is set
to the start of the task function. Once the stack has been initialised
the top of stack variable is updated. */
#if( portUSING_MPU_WRAPPERS == 1 )
{
// pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged );
}
#else /* portUSING_MPU_WRAPPERS */
{
/*给TCB任务结构体栈顶指针指向PC R寄存器,这函数就是吧栈的首地址存放PC等CPU运行时的数值*/
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
}
#endif /* portUSING_MPU_WRAPPERS */
/*任务句柄不等于空的时候*/
if( ( void * ) pxCreatedTask != NULL )
{
/* Pass the handle out in an anonymous way. The handle can be used to
change the created task's priority, delete the created task, etc.*/
/*把任务控制块赋值给任务句柄*/
*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
1.记录任务数量uxCurrentNumberOfTasks++
2.判断新创建的任务是否为第一个任务。如果创建的是第一个任务,初始化任务列表prvinitialiseTaskLists,如果创建的不是第一个任务,并且调度器还未开始启动,比较新任务与正在执行的一任务优先级大小,新任务优先级大的话,将当前控制块重新指向新的控制块
3.新的任务控制块添加到就緒列表中,便用函数prvAddTaskToReadyList
4.如果调度器已经开始运行,并且新任务的优先级更大的话,进行一次任务切换
static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )
{
/* Ensure interrupts don't access the task lists while the lists are being
updated. */
/*进入临界区*/
taskENTER_CRITICAL();
{
/*任务数量加1*/
uxCurrentNumberOfTasks++;
/*判断是不是第一个任务*/
if( pxCurrentTCB == NULL )
{
/* There are no other tasks, or all the other tasks are in
the suspended state - make this the current task. */
/*是第一任务,就把新创建的任务块赋值给它*/
pxCurrentTCB = pxNewTCB;
/*确认任务数量是不是1*/
if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )
{
/* This is the first task to be created so do the preliminary
initialisation required. We will not recover if this call
fails, but we will report the failure. */
/*初始化列表,就绪列表,2个延时列表,等待就绪列表等*/
prvInitialiseTaskLists();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
/*不是第一个任务*/
else
{
/* If the scheduler is not already running, make this task the
current task if it is the highest priority task to be created
so far. */
/*判断调度器是否启动*/
if( xSchedulerRunning == pdFALSE )
{
/*当前任务优先级是否小于等于新创建的优先级*/
if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
{
/*pxCurrentTCB始终指向当前任务优先级最高的任务块,
把新创建的任务控制块赋值给pxCurrentTCB*/
pxCurrentTCB = pxNewTCB;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
/*任务标号+1,就是第几个创建的*/
uxTaskNumber++;
#if ( configUSE_TRACE_FACILITY == 1 )
{
/* Add a counter into the TCB for tracing only. */
/*把任务标号赋值给TCB任务控制块,*/
pxNewTCB->uxTCBNumber = uxTaskNumber;
}
#endif /* configUSE_TRACE_FACILITY */
/*没有实现*/
traceTASK_CREATE( pxNewTCB );
/*添加到就绪列表*/
prvAddTaskToReadyList( pxNewTCB );
/*没有实现*/
portSETUP_TCB( pxNewTCB );
}
/*退出临界区*/
taskEXIT_CRITICAL();
/*不等于pdFALSE就是任务调度开启了*/
if( xSchedulerRunning != pdFALSE )
{
/* If the created task is of a higher priority than the current task
then it should run now. */
/*当前任务优先级是否小于,现在创建任务的优先级*/
if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority )
{
/*执行任务切换*/
taskYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
1.获取所要删除任务的控制块,通过传入的任务句柄,判断所需要删除哪个任务,NULL代表删除自身
2.将被删除任务,移除所在列表 将该任务在所在列表中移除,包括:就绪、阻塞、挂起、事件等列表
3.判断所需要删除的任务,删除任务自身,需先添加到等待删除列表,内存释放将在空闲任务执行,删除其他任务,当前任务数量-更新下一个任务的阻塞超时时间,以防被删除的任务就是下一个阻塞超时的任务
4.删除的任务为其他任务则直接释放内存prvDeleteTCB()
5.调度器正在运行且删除任务自身,则需要进行一次任务切换
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 list. */
/*移除任务优先级列表,在判断优先级列表是不是等于0*/
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
/*如果是等于0那就代表这个优先级没有任务,需要把32位的优先级变量清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. */
/*等待删除列表加1*/
++uxDeletedTasksWaitingCleanUp;
/* 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
{
/*任务数量-1*/
--uxCurrentNumberOfTasks;
prvDeleteTCB( pxTCB );
/* Reset the next expected unblock time in case it referred to
the task that has just been deleted. */
/*更新下一个阻塞任务超时时间,以防止被删除的任务就是下一个阻塞超时的任务*/
prvResetNextTaskUnblockTime();
}
/*没实现*/
traceTASK_DELETE( pxTCB );
}
/*退出临界区*/
taskEXIT_CRITICAL();
/*判断如果不是当前任务直接释放推栈空间*/
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();
}
}
}