任务创建和删除
1. 任务创建和删除API函数
- xTaskCreate()函数:动态创建一个新的任务,每个任务都需要RAM来保存任务状态(任务控制块+任务栈),此接口采用动态分配内存资源
BaseType_t xTaskCreate(TaskFunction_t pvTaskCode, //任务函数(函数名)
const char *const pcName, //任务名称(字符串)
unsigned short usStackDepth, //任务堆栈大小
void *pvParameters, //传递给任务函数的参数
UBaseType_t uxPriority, //任务优先级
TaskHandle_t *pxCreatedTask);//任务句柄
返回值:pdPASS:创建成功
errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY:堆空间不足,失败
/*注意:configSUPPORT_DYNAMIC_ALLOCATION 必须置为1*/
- xTaskCreateStatic()函数:静态创建一个新的任务,每个任务都需要RAM来保存任务状态(任务控制块+任务栈),此接口采用静态分配内存资源
TaskHandle_t xTaskCreateStatic(TaskFunction_t pvTaskCode,//任务函数(函数名)
const char *const pcName, //任务名称(字符串)
uint32_t ulStackDepth, //任务堆栈大小
void *pvParameters, //传递给任务函数的参数
UBaseType_t uxPriority, //任务优先级
StackType_t *const puxStackBuffer, //任务堆栈
StaticTask_t *const pxTaskBuffer);//任务控制块
返回值:NULL:任务创建失败
其他值:任务句柄
/*注意:configSUPPORT_STATIC_ALLOCATION 必须置为1*/
- 任务删除函数
函数原型:void vTaskDelete(TaskHandle_t xTaskToDelete)
参 数:xTaskToDelete 要删除的任务的任务句柄
返 回 值:无
2. 任务创建和删除函数源码分析
2.1 FreeRTOS任务创建函数源码分析
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint16_t usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask )
{
TCB_t *pxNewTCB;
BaseType_t xReturn;
#define portSTACK_GROWTH //(-1)表示满减栈
#if( portSTACK_GROWTH > 0 ){
}
#else{ /* portSTACK_GROWTH */
StackType_t *pxStack;
/* 任务栈内存分配*/
pxStack = ( StackType_t *) pvPortMalloc(((( size_t) usStackDepth ) * sizeof( StackType_t)));
if( pxStack != NULL ){
/* 任务控制块内存分配 */
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
if( pxNewTCB != NULL ){
/* 赋值栈地址 */
pxNewTCB->pxStack = pxStack;
}
else{
/* 释放栈空间 */
vPortFree( pxStack );
}
}
else{
/* 没有分配成功 */
pxNewTCB = NULL;
}
}
#endif /* portSTACK_GROWTH */
if( pxNewTCB != NULL )
{
/* 新建任务初始化 */
prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
/* 把任务添加到就绪列表中 */
prvAddNewTaskToReadyList( pxNewTCB );
xReturn = pdPASS;
}
else{
xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
}
return xReturn;
}
2.2 任务初始化函数源码分析
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;
/* 计算栈顶的地址 */
#if( portSTACK_GROWTH < 0 ){
/* 把栈空间的高地址分配给栈顶 */
pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
/* 栈对齐----栈要8字节对齐 */
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 */
/* 存储任务名称 */
for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ){
pxNewTCB->pcTaskName[ x ] = pcName[ x ];
if( pcName[ x ] == 0x00 ){
break;
}
else{
mtCOVERAGE_TEST_MARKER();
}
}
/* \0补齐字符串 */
pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';
/* 判断任务分配的优先级,是否大于最大值 如果超过最大值,赋值最大值 */
if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ){
uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
}
else{
mtCOVERAGE_TEST_MARKER();
}
/* 赋值任务优先级到任务控制块 */
pxNewTCB->uxPriority = uxPriority;
/* 任务状态表 事件表初始化 */
vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
vListInitialiseItem( &( pxNewTCB->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{ /* portUSING_MPU_WRAPPERS */
/* 任务堆栈初始化,之后返回任务栈顶 */
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
}
#endif /* portUSING_MPU_WRAPPERS */
if( ( void * ) pxCreatedTask != NULL ){
/* 赋值任务句柄 */
*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
}
else{
mtCOVERAGE_TEST_MARKER();
}
}
2.3 任务堆栈初始化函数源码分析
StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters){
pxTopOfStack--; /* 入栈程序状态寄存器 */
*pxTopOfStack = portINITIAL_XPSR; /* xPSR */
pxTopOfStack--; /* 入栈PC指针 */
*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; /* PC */
pxTopOfStack--; /* 入栈LR链接寄存器 */
*pxTopOfStack = ( StackType_t ) prvTaskExitError; /* LR */
pxTopOfStack -= 5; /* 跳过R12, R3, R2 and R1这四个寄存器,不初始化 */
*pxTopOfStack = ( StackType_t ) pvParameters; /* R0作为传参入栈 */
pxTopOfStack--; /* 异常返回值入栈 返回值是确定程序使用的栈地址是哪一个 MSP PSP*/
*pxTopOfStack = portINITIAL_EXEC_RETURN;
pxTopOfStack -= 8; /* 跳过R11, R10, R9, R8, R7, R6, R5 and R4这8个寄存器,不初始化 */
return pxTopOfStack; /*最终返回栈顶*/
}
以STM32(堆栈为向下增长模式)为例,经过上面的初始化后,此时的堆栈结果如下图所示
2.4 任务删除函数源码分析
void vTaskDelete( TaskHandle_t xTaskToDelete ){
TCB_t *pxTCB;
/* 进入临界段 */
taskENTER_CRITICAL();
{
/* 如果传入的参数为NULL,说明调用vTaskDelete的任务要删除自身 */
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 ){
/* 把任务添加到等待删除的任务列表中,并在空闲任务中删除 */
vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );
/* 记录有多少个任务需要释放内存 */
++uxDeletedTasksWaitingCleanUp;
/* 任务删除钩子函数---需要用户自己实现*/
portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending );
}
else{
/* 要删除的是别的任务 */
--uxCurrentNumberOfTasks;
prvDeleteTCB( pxTCB );
/* 重新计算还要多长时间执行下一个任务 */
prvResetNextTaskUnblockTime();
}
traceTASK_DELETE( pxTCB );
}
/* 退出临界段 */
taskEXIT_CRITICAL();
/* 判断调度器是否开启 */
if( xSchedulerRunning != pdFALSE ){
/* 如果是删除任务本身,马上进行任务调度(释放CPU的使用权)*/
if( pxTCB == pxCurrentTCB ){
configASSERT( uxSchedulerSuspended == 0 );
portYIELD_WITHIN_API();
}
else{
mtCOVERAGE_TEST_MARKER();
}
}
}
3. 任务创建和删除函数实例
本实验设计三个任务:
start_task:用来创建其他两个任务
task1_task:此任务运行5次后调用vTaskDelete函数删除任务task2_task,同时控制LED0的闪烁
task2_task:此任务控制控制LED1的闪烁
- 任务设置
#define START_TASK_PRIO 1 //任务优先级
#define START_STK_SIZE 128 //任务堆栈大小
TaskHandle_t StartTask_Handler; //任务句柄
void start_task(void *pvParameters); //任务函数
#define TASK1_TASK_PRIO 2 //任务优先级
#define TASK1_STK_SIZE 128 //任务堆栈大小
TaskHandle_t Task1Task_Handler; //任务句柄
void task1_task(void *pvParameters); //任务函数
#define TASK2_TASK_PRIO 3 //任务优先级
#define TASK2_STK_SIZE 128 //任务堆栈大小
TaskHandle_t Task2Task_Handler; //任务句柄
void task2_task(void *pvParameters); //任务函数
- main()函数
int main(void){
......
//创建开始任务
xTaskCreate((TaskFunction_t )start_task, //任务函数
(const char* )"start_task", //任务名称
(uint16_t )START_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )START_TASK_PRIO, //任务优先级
(TaskHandle_t* )&StartTask_Handler); //任务句柄
vTaskStartScheduler(); //开启任务调度
}
- 任务函数
/* 开始任务任务函数 */
void start_task(void *pvParameters){
taskENTER_CRITICAL(); //进入临界区
//创建 TASK1 任务
xTaskCreate((TaskFunction_t )task1_task,
(const char* )"task1_task",
(uint16_t )TASK1_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_TASK_PRIO,
(TaskHandle_t* )&Task1Task_Handler);
//创建 TASK2 任务
xTaskCreate((TaskFunction_t )task2_task,
(const char* )"task2_task",
(uint16_t )TASK2_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_TASK_PRIO,
(TaskHandle_t* )&Task2Task_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//task1 任务函数
void task1_task(void *pvParameters){
uint8_t task1_num=0;
while(1){
task1_num++; //任务1执行次数加1
LED0=!LED0;
printf("任务1已经执行: %d 次\r\n",task1_num);
if(task1_num==5){
vTaskDelete(Task2Task_Handler);//任务 1 执行 5 次删除任务 2 (4)
printf("任务 1 删除了任务 2!\r\n");
}
vTaskDelay(1000); //延时 1s,也就是 1000 个时钟节拍
}
}
//task2 任务函数
void task2_task(void *pvParameters){
uint8_t task2_num=0;
while(1){
task2_num++; //任务2执行次数加1
LED1=!LED1;
printf("任务 2 已经执行: %d 次\r\n",task2_num);
vTaskDelay(1000); //延时 1s,也就是 1000 个时钟节拍
}
}
编译程序并下载到开发板中,打开串口助手,显示如下图示:一开始任务1和任务2是同时运行的,由于任务2的优先级比任务1高,所以任务2先输出信息;任务1运行了5次以后任务1就删除了任务2,最后只剩下任务1在运行
关注我的公众号,在公众号里发如下消息,即可获取相应的工程源代码:
FreeRTOS任务创建和删除实例