任务创建/删除流程
1.简介
FreeRTOS 中任务创建通过 xTaskCreate()
或 xTaskCreateStatic()
实现。动态创建(xTaskCreate
)会自动分配任务栈和TCB(任务控制块),静态创建(xTaskCreateStatic
)需用户预分配内存。
动态创建流程:
- 参数校验:检查栈深度、任务函数指针等有效性。
- 内存分配:调用
pvPortMalloc
分配TCB和栈空间。 - TCB初始化:填充任务名、优先级、栈指针等字段。
- 栈初始化:调用
pxPortInitialiseStack
模拟中断压栈,构造初始上下文。 - 任务就绪:将任务加入就绪列表,触发调度(若调度已启动)。
任务删除流程:任务删除通过 vTaskDelete()
实现,支持删除其他任务或自身(传参 NULL
)。删除后资源需由空闲任务回收。
核心步骤:
- 任务状态检查:若删除自身标记为待删除状态并触发调度;否则直接从就绪/阻塞列表移除。
- 资源释放:TCB和栈内存由空闲任务通过
prvDeleteTCB()
释放。 - 调度触发:若删除高优先级任务,可能引发上下文切换。
2.任务控制块/链表项:
typedef struct tskTaskControlBlock
{volatile StackType_t * pxTopOfStack; #if ( portUSING_MPU_WRAPPERS == 1 )xMPU_SETTINGS xMPUSettings; #endifListItem_t xStateListItem; //链表项,后面会加入任务的状态链表ListItem_t xEventListItem; //链表项,后页面会加入信号量,消息队列等链表 UBaseType_t uxPriority; //任务优先级 StackType_t * pxStack; //任务栈的起点 char pcTaskName[ configMAX_TASK_NAME_LEN ]; //任务名,可以为空吧#if ( configUSE_TRACE_FACILITY == 1 ) //trace时使用UBaseType_t uxTCBNumber; UBaseType_t uxTaskNumber; #endif#if ( configUSE_MUTEXES == 1 ) //互斥时使用--初始优先级和继承优先级UBaseType_t uxBasePriority; UBaseType_t uxMutexesHeld;#endif#if ( configUSE_TASK_NOTIFICATIONS == 1 ) //任务通知使用--存放任务通知的值和状态volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];#endif} tskTCB;
struct xLIST_ITEM
{listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE configLIST_VOLATILE TickType_t xItemValue; //链表值,用于链表排序struct xLIST_ITEM * configLIST_VOLATILE pxNext; //指向后一个链表项struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; //指向前一个链表项void * pvOwner; //一般用于存放TCB指针 struct xLIST * configLIST_VOLATILE pxContainer; //用于存包含此链表项的链表的指针listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE
};
3.任务创建/删除(下文只分析动态创建的):
为什么推荐使用动态创建:
- 我是用的芯片为f407,ram空间为:192K的----包括64K的CCM(仅CPU可访问)、112K的SRAM1(主RAM)和16K的SRAM2(外设使用)。空间足够大可以直接用heap4管理。
- heap4提供了空间管理回收,若是自己分配还需要自己释放,检查是否溢出
/***************************************************************
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, //回调函数名/地址const char * const pcName, //任务名字const configSTACK_DEPTH_TYPE usStackDepth, //任务栈大小,单位4bytevoid * const pvParameters, //回调函形参UBaseType_t uxPriority, //任务优先级,数值越大,优先级越高TaskHandle_t * const pxCreatedTask ) //任务控制块地址/句柄
********************************************************************/
代码分析:
1.xTaskCreate
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,const char * const pcName, const configSTACK_DEPTH_TYPE usStackDepth,void * const pvParameters,UBaseType_t uxPriority,TaskHandle_t * const pxCreatedTask ){TCB_t * pxNewTCB;BaseType_t xReturn;#if ( portSTACK_GROWTH > 0 ) //增栈{************************************}#else //减栈{StackType_t * pxStack; /*-------------------------申请任务栈空间-----------------------*/ pxStack = pvPortMallocStack((((size_t)usStackDepth)*sizeof( StackType_t ) ) );if( pxStack != NULL ) //如果申请栈空间成功{ /*---------------------申请TCB空间-------------------*/ pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); if( pxNewTCB != NULL ) //如果TCB空间申请成功{memset((void*)pxNewTCB,0x00,sizeof(TCB_t)); //清空TCB空间pxNewTCB->pxStack = pxStack; //TCB->pxStack初始化}else {vPortFreeStack( pxStack );} //申请TCB空间失败}else pxNewTCB = NULL; //申请任务栈空间失败}#endif /* portSTACK_GROWTH */if(pxNewTCB != NULL) //TCB和栈空间都申请成功{prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL ); //初始化任务TCB以及栈prvAddNewTaskToReadyList( pxNewTCB ); //将任务添加到就绪链表xReturn = pdPASS;}else xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; //申请空间失败 return xReturn; //返回}
/*------------------------------------------------------------------------------------*/
1.1调用函数分析:
prvInitialiseNewTask()
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 )
{StackType_t * pxTopOfStack;UBaseType_t x;/*------------------------初始化任务栈,每byte都为0xa5,检查栈溢出使用----------------------*/#if ( tskSET_NEW_STACKS_TO_KNOWN_VALUE == 1 ){ (void)memset(pxNewTCB->pxStack,(int)tskSTACK_FILL_BYTE,(size_t)ulStackDepth * sizeof(StackType_t));}#endif
/*------------------------------------------------------------------------------------*/ /*------------------如果是满减栈:找出栈顶位置(高地址),对齐-------------------------------*/#if ( portSTACK_GROWTH < 0 ){pxTopOfStack = &(pxNewTCB->pxStack[ulStackDepth-(uint32_t)1]);pxTopOfStack = (StackType_t *)(((portPOINTER_SIZE_TYPE)pxTopOfStack)&(~(( portPOINTER_SIZE_TYPE)portBYTE_ALIGNMENT_MASK))); configASSERT(((portPOINTER_SIZE_TYPE)pxTopOfStack&(portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK)==0UL));}#else /* portSTACK_GROWTH */*****************************#endif /* portSTACK_GROWTH */
/*-----------------------------------------------------------------------------------*//*-------------------如果任务名不为NULL,TCB->pcTaskName初始化--------------------------*/if( pcName != NULL ){for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ){pxNewTCB->pcTaskName[ x ] = pcName[ x ]; if( pcName[ x ] == ( char ) 0x00 ){break;}else{mtCOVERAGE_TEST_MARKER();}} pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';}else{mtCOVERAGE_TEST_MARKER();}
/*----------------------------------------------------------------------------------*//*------------任务优先级设置:如果任务优先级大于最大优先级,设置为最大优先级configASSERT( uxPriority < configMAX_PRIORITIES );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 */
/*---------------------------------------------------------------------------------*//*------------链表项初始化,清除链表项中pxContainer,表示未加入任何链表------------------*/vListInitialiseItem( &( pxNewTCB->xStateListItem ) );vListInitialiseItem( &( pxNewTCB->xEventListItem ) );
/*---------------------------------------------------------------------------------*/ /*将TCB指针放入链表项StateListItem,EventListItem,优先级和最高优先级的差放入xEventListItem-*/ listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB ); listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority );listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB );
/*---------------------------------------------------------------------------------*/#if ( portUSING_MPU_WRAPPERS == 1 ){**********************************}#else ( void ) xRegions;#endif/*--------------------------------TCB初始化-------------------------------------*/ #if ( portUSING_MPU_WRAPPERS == 1 ){ ********************************}#else /* portUSING_MPU_WRAPPERS */{#if ( portHAS_STACK_OVERFLOW_CHECKING == 1 ){****************************************}#else //获取栈顶{pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );}#endif /* portHAS_STACK_OVERFLOW_CHECKING */}#endif
/*----------------------------TCB地址存入pxCreatedTask--------------------------*/if( pxCreatedTask != NULL ){*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;}else{mtCOVERAGE_TEST_MARKER();}
}
/*------------------------------------------------------------------------------------*/
prvAddNewTaskToReadyList( )
static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )
{taskENTER_CRITICAL(); //进入临界缓冲区{uxCurrentNumberOfTasks++;
/*-----------------------------------如果当前没有任务-----------------------------------*/if( pxCurrentTCB == NULL ){pxCurrentTCB = pxNewTCB;if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 ) //如果这是第一个任务{ prvInitialiseTaskLists();}else{mtCOVERAGE_TEST_MARKER();}}
/*---------------------------------------当前有的任务-----------------------------------*/else{ if( xSchedulerRunning == pdFALSE ) //如果没有开启调度{/*--如果新任务优先级高于或者等于当前任务,当前任务设置为新任务--*/if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority ){pxCurrentTCB = pxNewTCB;}else mtCOVERAGE_TEST_MARKER(); }else //如果开启调度,不需要更换当前任务{mtCOVERAGE_TEST_MARKER(); }}uxTaskNumber++; //统计任务数量#if ( configUSE_TRACE_FACILITY == 1 ){pxNewTCB->uxTCBNumber = uxTaskNumber; //TCB中记录自己序号}#endif /* configUSE_TRACE_FACILITY */traceTASK_CREATE( pxNewTCB );prvAddTaskToReadyList( pxNewTCB );portSETUP_TCB( pxNewTCB );}taskEXIT_CRITICAL(); //退出临界缓冲区if( xSchedulerRunning != pdFALSE ) //如果调度开启{ if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority ) //新加入任务优先级高{taskYIELD_IF_USING_PREEMPTION(); //唤醒任务切换-唤醒pendsv中断}else //新任务优先级低,不理会{mtCOVERAGE_TEST_MARKER();}}else //如果任务调度没开启,不予理会{mtCOVERAGE_TEST_MARKER();}
}
/*-----------------------------------------------------------*/
代码片段:
void vTaskDelete( TaskHandle_t xTaskToDelete ) {TCB_t *pxTCB;// 获取待删除任务的TCBpxTCB = prvGetTCBFromHandle( xTaskToDelete );// 从状态列表中移除uxListRemove( &( pxTCB->xStateListItem ) );// 如果是删除自身,标记为待删除并触发调度if( xTaskToDelete == NULL ) {vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );portYIELD();} else {prvDeleteTCB( pxTCB );}
}
2.xTaskCreate()
void vTaskDelete( TaskHandle_t xTaskToDelete ){TCB_t * pxTCB;taskENTER_CRITICAL(); //进入临界区{pxTCB = prvGetTCBFromHandle( xTaskToDelete ); //判断删除自身还是别的任务
/*------------------------------将任务从其状态链表中移除------------------------------*/if( uxListRemove(&(pxTCB->xStateListItem))==(UBaseType_t)0){taskRESET_READY_PRIORITY( pxTCB->uxPriority ); //将其从就绪链表中移除}else{mtCOVERAGE_TEST_MARKER();}
/*--------------------------------如果任务在等待事件---------------------------------*/if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ){( void ) uxListRemove( &( pxTCB->xEventListItem ) ); //将事件从其链表中移除}else{mtCOVERAGE_TEST_MARKER();}uxTaskNumber++; //任务数量依旧增加
/*--------------------------------如果待删除任务是当前任务---------------------------*/if( pxTCB == pxCurrentTCB ){/*-------任务放入终止链表中,后续在idle任务中删除--------*/vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );/*----增加此值,目的是idle任务可以知道有任务需要删除-----*/++uxDeletedTasksWaitingCleanUp; /*---window模式使用,arm不用理会----*/traceTASK_DELETE( pxTCB );portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending );}
/*-------------------------------如果待删除任务不是当前任务---------------------------*/else{--uxCurrentNumberOfTasks; //减少当前任务数量traceTASK_DELETE( pxTCB ); prvResetNextTaskUnblockTime(); //重置解除阻塞时间}}taskEXIT_CRITICAL(); //退出临界保护
/*----------------如果要删除的任务不是当前任务,直接删除TCB,释放空间--------------------*/if( pxTCB != pxCurrentTCB ){prvDeleteTCB( pxTCB );}
/*----------------------------如果调度器重新开始计时调度-----------------------------*/if( xSchedulerRunning != pdFALSE ){if( pxTCB == pxCurrentTCB ){configASSERT( uxSchedulerSuspended == 0 );portYIELD_WITHIN_API();}else{mtCOVERAGE_TEST_MARKER();}}}
关键函数说明
prvInitialiseNewTask()
:初始化TCB结构,包括栈顶指针、任务入口、优先级链表等。prvAddNewTaskToReadyList()
:将任务加入就绪列表,若优先级高于当前任务则触发调度。prvDeleteTCB()
:释放TCB和栈内存(动态创建时),静态创建需用户手动释放。
注意事项
- 删除任务时需确保其未持有信号量、队列等资源,否则可能导致内存泄漏。
- 静态创建任务需保证预分配内存生命周期覆盖任务运行周期。