当前位置: 首页 > news >正文

【FreeRTOS】03任务管理

1、任务管理的相关基础概念

1.1、 任务

从FreeRTOS系统的角度看,任务是竞争系统资源的最小运行单元,有自己的运行环境。任务可以使用或等待CPU、使用内存空间等系统资源,并独立于其它任务运行。一个系统能运行多少个任务,取决于系统有多少可用的SRAM。
实际上任务就是一个无限循环且不带返回值的C函数。
1、任务的基本信息:

  • 任务入口函数:记录任务的入口函数地址
  • 任务名称:标识任务的字符串。
  • 任务栈:用于保护任务切换时的上下文。
  • 任务优先级:用于表示任务优先执行的等级。数字越大,优先级越高。
  • 任务控制块指针:是操作系统用于管理任务的核心数据结构。(任务间通讯、状态管理、获取任务运行信息)
  • 任务状态:记录任务当前所处的状态,是调度器判断任务是否可以执行的依据。

2、动态任务和静态任务
在FreeRTOS中,静态任务和动态任务是根据任务创建时内存分配方式区分的两种任务类型,核心差异在于任务控制块(TCB)和任务堆栈的内存是由用户静态分配的还是内核动态分配的。
静态任务

  • 内存分配方式:任务创建前,由用户静态预先分配任务控制块(TCB)和任务堆栈的内存,创建时将内存读地址传递给内核。
  • 创建函数:使用xTaskCreaateStatic()创建。
  • 内存释放:任务删除后,内存不会被内核自动释放,需要用户手动管理。
static StackType_t LED_Task_Stack[128]; 	/* LED任务堆栈 */ 
static StaticTask_t LED_Task_TCB; 			/*  任务控制块 */ 
static void LED_Task(void* pvParameters);	/* LED_Task任务实现 */ /*********************************************************************** 
* @ 函数名  : AppTaskCreate 
* @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面 
* @ 参数    : 无 
* @ 返回值  : 无 
***************************************************************/ 
static void AppTaskCreate(void) 
{ taskENTER_CRITICAL();           //进入临界区 /* 创建LED_Task任务 */ LED_Task_Handle = xTaskCreateStatic((TaskFunction_t )LED_Task, //任务函数 (const char*)"LED_Task",//任务名称 (uint32_t)128, //任务堆栈大小 (void*  )NULL, //传递给任务函数的参数 (UBaseType_t)4, //任务优先级 (StackType_t*)LED_Task_Stack,//任务堆栈 (StaticTask_t*)&LED_Task_TCB);//任务控制块 if (NULL != LED_Task_Handle) /* 创建成功 */ printf("LED_Task任务创建成功!\n"); else printf("LED_Task任务创建失败!\n"); vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务 taskEXIT_CRITICAL();            //退出临界区 
}/************************************************************** 
* @ 函数名  : LED_Task 
* @ 功能说明: LED_Task任务主体 
* @ 参数    : 
* @ 返回值  : 无 
********************************************************************/ 
static void LED_Task(void* parameter) 
{ while (1) { LED1_ON; vTaskDelay(500);   /* 延时500个tick */ printf("led1_task running,LED1_ON\r\n"); LED1_OFF; vTaskDelay(500);   /* 延时500个tick */ printf("led1_task running,LED1_OFF\r\n"); } 
}

动态任务

  • 内存分配方式:任务创建时,由FreeRTOS内核通过pvPortMalloc()从堆中动态分配任务控制块和任务堆栈的内存。
  • 创建函数:通过xTaskCreat()创建。
  • 删除函数:通过vTaskDelete()删除。删除后,内核自动通过vPortFree()释放分配的内存。
static TaskHandle_t LED_Task_Handle = NULL; /* LED任务句柄 */ /*********************************************************************** 
* @ 函数名  : AppTaskCreate
* @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面 
* @ 参数    : 无 
* @ 返回值  : 无  
**********************************************************************/ 
static void AppTaskCreate(void) 
{ BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */ taskENTER_CRITICAL();           //进入临界区 /* 创建LED_Task任务 */ xReturn = xTaskCreate((TaskFunction_t )LED_Task, /* 任务入口函数 */ (const char*    )"LED_Task",/* 任务名字 */ (uint16_t       )512,   /* 任务栈大小 */ (void*          )NULL, /* 任务入口函数参数 */ (UBaseType_t    )2,     /* 任务的优先级 */ (TaskHandle_t*  )&LED_Task_Handle);/* 任务控制块指针 */ if (pdPASS == xReturn) printf("创建LED_Task任务成功!\r\n"); vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务 taskEXIT_CRITICAL();            //退出临界区 
} /********************************************************************** 
* @ 函数名  : LED_Task 
* @ 功能说明: LED_Task任务主体 
* @ 参数    : 
* @ 返回值  : 无 
********************************************************************/ 
static void LED_Task(void* parameter) 
{ while (1) { LED1_ON; vTaskDelay(500);   /* 延时500个tick */ printf("led1_task running,LED1_ON\r\n"); LED1_OFF; vTaskDelay(500);   /* 延时500个tick */ printf("led1_task running,LED1_OFF\r\n"); } 
}

1.2、任务调度器

  • FreeRTOS中提供的任务调度器是基于优先级的全抢占式调度:在系统中除了中断处理函数、调度器上锁部分的代码和禁止中断的代码是不可抢占的之外,系统的其它部分都是可以抢占的。
  • 系统中任务优先级数值越小,优先级越低。0为最低优先级,分配给空闲任务使用。
  • 相同优先级的任务采用时间片轮转的方式调度。

1.3、任务状态与迁移

FreeRTOS系统中的每一个任务都有多种运行状态,完成任务创建和调度后,创建的任务就可以在系统中竞争一定的资源。
任务状态可以分为以下几种:

  • 就绪(Ready):任务在就绪列表中,该任务已经具备执行的条件,只能调度器进行调度,新创建的任务会初始化为就绪态。
  • 运行(Running):表明任务处于执行中,正在占用处理器。
  • 阻塞(Blocked):如果任务当前正在等待没某个时序或外部中断,该任务就处于阻塞状态。包含任务被挂起、任务被延时、任务正在等待信号量、读写队列或等待读写事件等。
  • 挂起态(Suspended):处于挂起状态的任务对调度器而言是不可见的,需要人为进行挂起和恢复。
    在这里插入图片描述
  • (1)创建任务—>就绪态:任务创建完成后进入就绪态,表明任务已经准备就绪,随时可以运行,只等待调度器进行调度。
  • (2)就绪态—>运行态:发生任务切换时,就绪列表中最高优先级的任务被执行,从而进入运行态。
  • (3)运行态—>就绪态:有更高优先级的任务创建或者恢复后,会发生任务调度,此刻就绪列表中最高优先级任务变成运行态,原来运行的任务会由运行态转变为就绪态。
  • (4)运行态—>阻塞态:正在运行的任务发生阻塞(挂起、延时、读信号量等待)时,该任务就会从就绪列表中删除,任务状态有运行态变成阻塞态,然后发生任务切换,运行就绪列表中当前最高优先级的任务。
  • (5)阻塞态—>就绪态:阻塞的任务被恢复后(任务恢复、延时时间超时、读信号量超时或读到信号量等),此时被恢复的任务会被加入就绪列表,从而由阻塞态变成就绪态;如果此时被恢复任务的优先级高于正在运行任务的优先级,则会发生任务切换,将该任务由就绪态转换为运行态。
  • (6)、(7)、(8)::就绪态、阻塞态、运行态→挂起态(Suspended):任务可以通
    过调用 vTaskSuspend() API 函数都可以将处于任何状态的任务挂起,被挂起的任务得不到
    CPU的使用权,也不会参与调度,除非它从挂起态中解除。
  • (9):挂起态→就绪态:把 一 个 挂 起 状态 的 任 务 恢复的 唯 一 途 径 就 是
    调 用 vTaskResume() vTaskResumeFromISR() API 函数,如果此时被恢复任务的优先级高
    于正在运行任务的优先级,则会发生任务切换,将该任务将再次转换任务状态,由就绪态
    变成运行态。

2、常见的任务函数

2.1、任务的挂起和恢复

  • 任务挂起:挂起指定任务,被挂起的任务不会得到CPU的使用权。
  • 任务恢复:将挂起的任务添加到就绪列表,并将任务状态更改为就绪状态。
void vTaskSuspend( TaskHandle_t xTaskToSuspend ) ;	/* 挂起任务,参数为null时,表示挂起自身 */
void vTaskResume( TaskHandle_t xTaskToResume ) ;	/* 恢复任务 */
void vTaskSuspendAll( void );	/* 将调度器(所有任务)挂起 */
void xTaskResumeAll(void);		/* 恢复调度器 */
BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) ; /* 在中断服务程序中恢复被挂起任务 */

2.2、任务删除

  • 任务删除:用于删除一个任务。当一个任务删除另外一个任务时,形参为要删除任务创建时返回的任务句柄,如果是删除任务自身,形参为NULL。
void vTaskDelete( TaskHandle_t xTaskToDelete ) ;

2.3、任务延时

阻塞延时:调用后,任务进入阻塞状态,让出CPU资源。

  • vTaskDelay:相对延时,指定的延时时间是从调用vTaskDelay()结束后开始计算的,经过指定的时间后延时结束。
  • vTaskDelayUntil:绝对延时,指定的延时时间是从上一次任务运行开始到下一次运行开始的时间间隔是绝对的。
void vTaskDelay( const TickType_t xTicksToDelay ) ;
void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement ) ;

3、任务的设计要点

我们要对自己设计的嵌入式系统要了如指掌,任务的优先级信息,任务与中断的处理,任务的运行时间、逻辑、状态等都要知道,才能设计出好的系统。
在设计之初就应该考虑下面几点因素:任务运行的上下文环境、任务的执行时间合理设计。

  • 1、中断服务函数:它运行在非任务的执行环境下(一般为芯片的一种特殊运行模式(也被称作特权模式)),在这个上下文环境中不能使用挂起当前任务的操作,不允许调用任何会阻塞运行的API函数接口。中断服务程序最好保持精简短小,快进快出,一般在中断服务函数中只做标记事件的发生,然后通知任务,让对应任务去执行相关处理。
  • 2、普通任务:任务不允许有死循环(无阻塞机制)
  • 3、 空闲任务:是FreeRTOS系统中没有其它工作时,自动进入的系统任务。空闲任务一般在调用vTaskStartScheduler()时,调度器会自动创建一个空闲任务。用户可以通过空闲任务钩子方式,在空闲任务上钩上自己的功能函数。
  • 4、任务执行时间:一般为任务从开始到结束的时间,以及任务的周期。
http://www.dtcms.com/a/281886.html

相关文章:

  • 工业相机GigE数据接口的优势及应用
  • django安装、跨域、缓存、令牌、路由、中间件等配置
  • Jenkins全方位CI/CD实战指南
  • LabVIEW Occurrence功能
  • 嵌入式Linux(RV1126)系统定制中的编译与引导问题调试报告
  • 【RTSP从零实践】12、TCP传输H264格式RTP包(RTP_over_TCP)的RTSP服务器(附带源码)
  • 基于WebRTC技术实现一个在线课堂系统
  • el-input 回显怎么用符号¥和变量拼接展示?
  • Spring Boot 解决跨域问题
  • Spring Boot - Spring Boot 集成 MyBatis 分页实现 手写 SQL 分页
  • 日语学习-日语知识点小记-构建基础-JLPT-N3阶段(5):语法+单词
  • Buffer Pool
  • css 如何实现大屏4个占位 中屏2个 小屏幕1个
  • Samba服务器
  • Git版本控制完全指南:从入门到精通
  • 网络编程/Java面试/TCPUDP区别
  • 基于spring boot养老院老人健康监护平台设计与实现
  • SFT:大型语言模型专业化定制的核心技术体系——原理、创新与应用全景
  • docker run elasticsearch 报错
  • JAVA面试宝典 -《分布式ID生成器:Snowflake优化变种》
  • 详解SPFA算法-单源最短路径求解
  • C++ - 仿 RabbitMQ 实现消息队列--sqlite与gtest快速上手
  • 基于springboot+vue的酒店管理系统设计与实现
  • 一叶障目不见森林
  • 身份证号码姓名认证解决方案-身份证三要素API接口
  • Apache IoTDB(1):时序数据库介绍与单机版安装部署指南
  • 更灵活方便的初始化、清除方法——fixture【pytest】
  • QT跨平台应用程序开发框架(9)—— 容器类控件
  • 城市守护者的蓝色印记
  • Qt小组件 - 5 图片懒加载样例