任务创建/删除流程

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和栈内存(动态创建时),静态创建需用户手动释放。
    注意事项
    • 删除任务时需确保其未持有信号量、队列等资源,否则可能导致内存泄漏。
    • 静态创建任务需保证预分配内存生命周期覆盖任务运行周期。

    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
    如若转载,请注明出处:http://www.pswp.cn/bicheng/89775.shtml
    繁体地址,请注明出处:http://hk.pswp.cn/bicheng/89775.shtml
    英文地址,请注明出处:http://en.pswp.cn/bicheng/89775.shtml

    如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

    相关文章

    warning: _close is not implemented and will always fail

    相关问题&#xff1a; 一、undefined reference to _exit undefined reference to ‘end‘ warning: _close is not implemented and will always fail 一、环境&#xff1a; ubuntu24.04实体机、 arm-none-eabi-gcc gcc version 13.2.1 20231009 (15:13.2.rel1-2) 二…

    MyBatis之缓存机制详解

    MyBatis之缓存机制详解一、MyBatis缓存的基本概念1.1 缓存的核心价值1.2 MyBatis的两级缓存体系二、一级缓存&#xff08;SqlSession级别缓存&#xff09;2.1 工作原理2.2 实战案例&#xff1a;一级缓存演示2.2.1 基础用法&#xff08;默认开启&#xff09;2.2.2 一级缓存失效场…

    云服务器搭建自己的FRP服务。为什么客户端的项目需要用Docker启动,服务端才能够访问到?

    简单回答&#xff1a;在云服务器搭建FRP服务时&#xff0c;客户端项目用Docker启动并非必需&#xff0c;而是因为Docker的特性简化了配置&#xff1a; Docker通过端口映射&#xff08;如-p 本地端口:容器端口&#xff09;能固定项目对外暴露的端口&#xff0c;减少本地端口冲突…

    6 STM32单片机的智能家居安防系统设计(STM32代码+手机APP设计+PCB设计+Proteus仿真)

    系列文章目录 文章目录 系列文章目录前言1 资料获取与演示视频1.1 资料介绍1.2 资料获取1.3 演示视频 2 系统框架3 硬件3.1 主控制器3.2 显示屏3.3 WIFI模块3.4 DHT11温湿度传感器3.5 烟雾/燃气传感器模块&#xff1a;MQ-23.6 火焰传感器3.7 门磁模块MC-38 4 设计PCB4.1 安装下…

    DevOps落地的终极实践:8大关键路径揭秘!

    本文来自腾讯蓝鲸智云社区用户: CanWay当前&#xff0c;DevOps因其能够降低IT运营成本、提高软件质量并加快上市时间的能力而在全球范围内引起广泛关注。它打破了传统软件开发与运营的界限&#xff0c;消除了新功能发布延迟和软件质量下降的障碍。DevOps通过实施持续集成、持续…

    react - 根据路由生成菜单

    后端返回菜单的格式menuList:[{index: true,name: "",component: "../views/Home",meta: { title: "首页", requiresAuth: true,roles:[user]},},{path: "/admin",name: "admin",meta: { title: "管理页", roles:…

    Window延迟更新10000天配置方案

    1.点击"开始"菜单&#xff0c;搜索"注册表编辑器"&#xff0c;点击"打开"。2.找到"\HKEY LOCAL MACHINE\SOFTWARE\Microsoft\WindowsUpdate\Ux\Settings"路径。3.右面空白处右键新建一个32位值&#xff0c;命名为FlightSettingsMaxPau…

    【OD机试】人民币转换

    题目描述 将阿拉伯数字金额转换为中文大写金额格式,需遵循以下规则: 1、 前缀要求:中文大写金额前必须标明“人民币”字样。 2、 用字规范:使用壹、贰、叁、肆、伍、陆、柒、捌、玖、拾、佰、仟、万、亿、元、角、分、零、整等字样。 3、 “整”字规则: 金额到“元”为止…

    在ajax中什么时候需要将返回值类型做转换

    $.ajax({url: TMSPROC0050/deleteData?accidentIds accidentIds.join(,),type: DELETE,dataType: json,success: function(result) {$(#accidentGrid).datagrid(reload);$.messager.show({title: 成功,msg: result.message})},error: function(result) {$.messager.alert({ti…

    Helm常用命令大全(2025最新版)

    文章目录Helm常用命令大全&#xff08;2025最新版&#xff09;一、基础命令与环境配置版本与帮助信息安装与升级HelmLinux系统安装版本升级注意事项二、仓库管理命令仓库基础操作OCI仓库支持&#xff08;v3.8新特性&#xff09;三、Chart操作命令Chart创建与打包Chart搜索与下载…

    gitlab+jenkins

    文章目录架构gitlab和jenkins安装jenkins配置gitlab配置jenkins与gitlab联动参考架构 gitlab和jenkins安装 部署docker 部署jenkins 启动jenkins 用户&#xff1a;admin&#xff0c;对应的密码如下 点击安装自定义推荐的插件 安装gitlab插件 jenkins配置 配置pipline…

    Redis字符串操作指南:从入门到实战应用

    Redis作为一款高性能的键值存储数据库&#xff0c;其字符串&#xff08;String&#xff09;类型是最基础也最常用的数据类型。它不仅能存储简单的文本信息&#xff0c;还能应对数字计算、二进制数据等多种场景&#xff0c;灵活且高效。接下来&#xff0c;我们就全方位剖析Redis…

    SQLite 数据库字段类型-详细说明,数据类型详细说明。

    SQLite 数据类型 SQLite字段类型详细说明&#xff0c;包含存储类、亲和类型、布尔类型、日期时间类型的存储方式、取值范围及核心特性。 创建 SQLite3 表时可使用的各种数据类型名称&#xff0c;同时也介绍了相应的亲和类型。 一、核心存储类&#xff08;Storage Classes&am…

    Node.js特训专栏-实战进阶:17.会话管理与安全存储

    🔥 欢迎来到 Node.js 实战专栏!在这里,每一行代码都是解锁高性能应用的钥匙,让我们一起开启 Node.js 的奇妙开发之旅! Node.js 特训专栏主页 专栏内容规划详情 会话管理与安全存储:从原理到实战的Web安全实践 在Web应用中,会话(Session)是维持用户状态的核心机制—…

    【橘子分布式】gRPC(编程篇-中)

    一、简介 我们之前已经完成了对于api模块的开发&#xff0c;也就是已经生成了基础的类和对应的接口&#xff0c;现在我们需要完成的是client和server端的开发。其实如同thrift一样&#xff0c;现在要做的就是实现我们之前定义的service里面的hello方法&#xff0c;里面写我们的…

    Spring Boot 项目中数据同步之binlog和MQ

    在 Spring Boot 项目中&#xff0c;“监听 binlog” 和 “业务代码中集成 MQ” 是实现数据同步、事件驱动的两种主流方法。 简单来说&#xff0c;这个选择可以概括为&#xff1a; 监听 Binlog (如使用 Canal)&#xff1a;像一个数据库的贴身秘书&#xff0c;它忠实地记录数据库…

    MySQL 写入性能优化全攻略(附 GitHub 面试题项目链接)

    面试中你可能会遇到这样的问题&#xff1a; &#x1f4ac; “假设你的接口一天收到百万级请求&#xff0c;MySQL 撑得住吗&#xff1f;你会怎么优化写入性能&#xff1f;” 刚开始我也懵过&#xff0c;后来不断复盘与总结&#xff0c;现在我可以用结构化方式给出一个相对完整的…

    用Dynamic chunk去干掉tokenizer?

    一般你们下AR模型的时候&#xff0c;都有这个&#xff0c;也就是tokenzier&#xff0c;tokenizer是干啥的&#xff0c;其实就是你的分词字典不光有specal的token对应的还有实际的对应的分词对应的代码&#xff0c;比如&#xff1a;也有tokenzier没显示的&#xff0c;比如&#…

    Linux系统日志管理入门:journalctl命令完全指南

    Linux系统日志管理入门&#xff1a;journalctl命令完全指南前言一、journalctl介绍二、基础使用&#xff1a;快速上手1. 查看全部日志2. 查看本次启动的日志3. 按时间筛选日志4. 按服务&#xff08;单元&#xff09;过滤日志三、常用参数与场景四、实战案例&#xff1a;解决实际…

    神经网络的基本骨架——nn.Module的使用(torch.nn库)

    在 PyTorch 中&#xff0c;nn.Module 是所有神经网络模块的基类&#xff0c;用于构建和组织深度学习模型。它提供了一系列工具和功能&#xff0c;使模型的定义、训练和部署更加高效和灵活。nn Neural Network&#xff08;神经网络&#xff09;核心作用&#xff1a;模块化设计&…