前言

资源:

任务概念

进程:进程是程序执行的过程,是程序在执行过程中分配和管理资源的基本单位。拥有独立的虚拟地址空间。

线程:线程是CPU调度和分派的基本单位。与其它同一进程的线程共享当前进程资源。

协程:比线程更加轻量级的存在,不是由操作系统内核管理,而是由程序控制的。其实就是在同一线程内时分地执行不同的子程序。(注意:不是函数调用)

还有管程、纤程。

并发:多个任务看起来是同时进行, 这是一种假并行。

并行:并行是指令同一时刻一起运行。

对于目前主流的RTOS的任务,大部分都属于并发的线程。

因为MCU上的资源每个任务都是共享的,可以认为是单进程多线程模型。

任务状态

freertos有四种状态,每种状态都有对应的状态链表管理。

运行态:占用CPU使用权时的状态。

就绪态:能够运行(没有被阻塞和挂起),但是当前没有运行的任务的状态。

阻塞态:由于等待信号量、消息队列、事件标志组、调用延迟函数等而处于的状态被称之为阻塞态。

挂起态:调用函数vTaskSuspend()对指定任务进行挂起,挂起后这个任务将不被执行。

  • 调用函数xTaskResume()可退出挂起状态。
  • 不可以指定超时周期事件(不可以通过设定超时事件而退出挂起状态)

任务状态转换图:

任务优先级

每个任务被分配一个从0到(configMAX_PRIORITIES - 1)的优先级。

configMAX_PRIORITIES 是在 FreeRTOSConfig.h文件中被定义。

优先级数值越高,优先级越高。

idle任务的优先级为0。

多个任务可以共享一个任务优先级。

如果在FreeRTOSConfig.h文件中配置宏定义configUSE_TIME_SLICING为1,或者没有配置此宏定义,时间片调度都是使能的。

使能时间片后,处于就绪态的多个相同优先级任务将会以时间片切换的方式共享处理器。

如果硬件架构支持CLZ指令,可以使用该特性,使能配置如下:

  1. FreeRTOSConfig.hconfigUSE_PORT_OPTIMISED_TASK_SELECTION设置为1;
  2. 最大优先级数目configMAX_PRIORITIES不能大于CPU位数。

空闲任务和空闲任务钩子

空闲任务

空闲任务是启动RTOS调度器时由内核自动创建的任务,其优先级为0,确保系统中至少有一个任务在运行。

空闲任务可用来释放RTOS分配给被删除任务的内存。

空闲任务钩子

空闲任务钩子是一个函数,每一个空闲任务周期被调用一次。

空闲任务钩子应该满足一下条件:

  1. 不可以调用可能引起空闲任务阻塞的API函数;
  2. 不应该陷入死循环,需要留出部分时间用于系统处理系统资源回收。

创建空闲钩子

FreeRTOSConfig.h头文件中设置configUSE_IDLE_HOOK为1;

定义一个函数,名字和参数原型如下所示:

void vApplicationIdleHook( void ); // FreeRTOS 规定了函数的名字和参数

一般设置CPU进入低功耗模式都是使用空闲任务钩子函数实现的。

创建任务

任务的创建有两种:创建静态内存任务和创建动态内存任务。

任务参数相关概念

任务入口函数:即是任务函数,是该任务需要跑的函数。

任务名称:即是任务名,主要用于调试。

任务堆栈大小:即是任务栈大小,单位是word。

任务入口函数参数:传递给任务入口函数的参数。在任务函数里,通过形参获得。

任务控制块:主要用于内核管理任务,记录任务信息。

任务句柄:用于区分不同的任务,用于找到该任务的任务控制块。

创建静态内存任务

xTaskCreateRestrictedStatic(),该函数不讲解,因为需要MPU,想研究的同学可以参考:freertos官网API

配置静态内存

创建静态内存任务需要先实现以下内容:

  1. 需要在FreeRTOSConfig.h打开configSUPPORT_STATIC_ALLOCATION宏,开启静态内存。

  2. 开启静态内存的同时需要实现两个函数:(使用静态内存分配任务堆栈和任务控制块内存)

    1. vApplicationGetIdleTaskMemory():空闲任务堆栈函数。
    2. vApplicationGetTimerTaskMemory():定时器任务堆栈函数。
  3. 注意静态内存对齐。

实现空闲任务堆栈函数

实现该函数是为了给内核提供空闲任务关于空闲任务控制块和空闲任务堆栈的相关信息。

/* 空闲任务控制块 */
static StaticTask_t Idle_Task_TCB;
/* 空闲任务任务堆栈 */
static StackType_t Idle_Task_Stack[configMINIMAL_STACK_SIZE]; /** @brief vApplicationGetIdleTaskMemory
* @details 获取空闲任务的任务堆栈和任务控制块内存
* @param
* @retval
* @author lizhuming
*/
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
uint32_t *pulIdleTaskStackSize)
{
*ppxIdleTaskTCBBuffer = &Idle_Task_TCB; /* 任务控制块内存 */
*ppxIdleTaskStackBuffer = Idle_Task_Stack; /* 任务堆栈内存 */
*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE; /* 任务堆栈大小 */
}

实现定时器任务堆栈函数

实现该函数是为了给内核创建定时器任务时提供定时器任务控制块和定时器任务堆栈的相关信息。

/* 定时器任务控制块 */
static StaticTask_t Timer_Task_TCB;
/* 定时器任务堆栈 */
static StackType_t Timer_Task_Stack[configTIMER_TASK_STACK_DEPTH]; /** @brief vApplicationGetTimerTaskMemory
* @details 获取定时器任务的任务堆栈和任务控制块内存
* @param
* @retval
* @author lizhuming
*/
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
StackType_t **ppxTimerTaskStackBuffer,
uint32_t *pulTimerTaskStackSize)
{
*ppxTimerTaskTCBBuffer = &Timer_Task_TCB;/* 任务控制块内存 */
*ppxTimerTaskStackBuffer = Timer_Task_Stack;/* 任务堆栈内存 */
*pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;/* 任务堆栈大小 */

配置内存对齐

内存对齐的配置在portmacro.h里面的portBYTE_ALIGNMENT宏,按自己需求配置即可。

在任务堆栈初始化时会把栈顶指针纠正为内存对齐。参考下列代码:

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 ) );

分配静态内存

静态内存分配是有编译器决定的。

在freertos中,创建任务需要分配的内存主要是任务控制块和任务堆栈。

/* 任务控制快 */
static StaticTask_t lzmStaticTestTaskTCB = {0};
/* 任务堆栈 */
static StackType_t lzmStaticTestTaskStack[256] = {0};

创建任务原型

创建任务函数原型:

TaskHandle_t xTaskCreateStatic( // 返回任务句柄
TaskFunction_t pxTaskCode, // 任务入口函数
const char * const pcName, // 任务名称
const uint32_t ulStackDepth, // 任务堆栈大小
void * const pvParameters, // 传递给任务入口函数的参数
UBaseType_t uxPriority, // 任务优先级
StackType_t * const puxStackBuffer, // 任务堆栈
StaticTask_t * const pxTaskBuffer ) // 任务控制块

创建任务

/* 创建静态内存任务 */
lzmStaticTestTaskHandle = xTaskCreateStatic((TaskFunction_t) lzmStaticTestTask, // 任务入口函数
(const char*) "lzm static test task", // 任务函数名
(uint32_t )256, // 任务堆栈大小
(void* )NULL, // 传递给任务入口函数的参数
(UBaseType_t)5, // 任务优先及
(StackType_t* )lzmStaticTestTaskStack, // 任务堆栈地址
(StaticTask_t* )&lzmStaticTestTaskTCB); // 任务控制块地址

创建动态内存任务

配置动态内存

动态内存配置是在FreeRTOSConfig.h配置的,这些内存主要供给FreeRTOS动态内存分配函数使用。

#define configTOTAL_HEAP_SIZE	( ( size_t ) ( 32 * 1024 ) ) // 系统总堆大小

而freertos的动态内存管理是有文件heap_x.c实现的,具体实现算法,后面讲到内存时会分析。

uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; // 系统总堆

任务句柄

static TaskHandle_t lzmTestTaskHandle = NULL;

创建任务原型

创建任务函数原型:

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 ) // 任务控制块指针

创建任务

/* 创建动态内存任务 */
xReturn = xTaskCreate((TaskFunction_t) lzmTestTask, // 任务入口函数
(const char*) "lzm test task", // 任务函数名
(uint16_t )256, // 任务堆栈大小
(void* )NULL, // 传递给任务入口函数的参数
(UBaseType_t)5, // 任务优先及
(TaskHandle_t* )&lzmTestTaskHandle); // 任务句柄

删除任务

配置删除任务

在文件FreeRTOSConfig.h中,必须定义宏INCLUDE_vTaskDelete 为 1,删除任务的API才会失效。

调用API删除任务后,将会从就绪、阻塞、暂停和事件列表中移除该任务。

如果是动态内存创建任务,删除任务后,其占用的空间资源有空闲任务释放,所以删除任务后尽量保证空闲任务获取一定的CPU时间。

如果是静态内存创建任务,删除任务后,需要自己处理释放任务占用的空间资源。

删除任务原型

void vTaskDelete( TaskHandle_t xTaskToDelete ); // 参数为任务句柄

注意:传入的参数为任务句柄,当出入的参数为NULL时,表示删除调用者当前的任务。

实战

源码:拉取 freertos_on_linux_task_01 文件夹

结果:

【freertos】003-任务基础知识的更多相关文章

  1. FreeRTOS学习笔记——FreeRTOS 任务基础知识

    RTOS 系统的核心就是任务管理,FreeRTOS 也不例外,而且大多数学习RTOS 系统的工程师或者学生主要就是为了使用RTOS 的多任务处理功能,初步上手RTOS 系统首先必须掌握的也是任务的创建 ...

  2. FreeRTOS基础知识

    前面一篇文章介绍了一些命名规范之类的基础知识,但是我觉得还缺少一定前言知识,就是裸机和操作系统有什么区别,为什么我们需要学freertos,因为招聘要求?那么为什么招聘网又会有这个要求呢?所以我们为什 ...

  3. JAVA学习基础知识总结(原创)

    (未经博主允许,禁止转载!) 一.基础知识:1.JVM.JRE和JDK的区别: JVM(Java Virtual Machine):java虚拟机,用于保证java的跨平台的特性. java语言是跨平 ...

  4. Spring笔记01(基础知识)

    1.基础知识 01.Spring:轻量级Java EE开源框架,它是由Rod Johnson为了解决企业应用程序开发的复杂性而创建. 02.目标:实现一个全方位的整合框架,实现“一站式”的企业应用开发 ...

  5. Vue学习之--------消息订阅和发布、基础知识和实战应用(2022/8/24)

    文章目录 1.基础知识 2.代码实例 2.1 main.js 2.2 School.vue 2.3 Student.vue 2.4 App.vue 3.全局事件总线通信改为消息的订阅和发布 3.1 核 ...

  6. .NET面试题系列[1] - .NET框架基础知识(1)

    很明显,CLS是CTS的一个子集,而且是最小的子集. - 张子阳 .NET框架基础知识(1) 参考资料: http://www.tracefact.net/CLR-and-Framework/DotN ...

  7. RabbitMQ基础知识

    RabbitMQ基础知识 一.背景 RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现.AMQP 的出现其实也是应了广大人民群众的需求,虽然 ...

  8. Java基础知识(壹)

    写在前面的话 这篇博客,是很早之前自己的学习Java基础知识的,所记录的内容,仅仅是当时学习的一个总结随笔.现在分享出来,希望能帮助大家,如有不足的,希望大家支出. 后续会继续分享基础知识手记.希望能 ...

  9. selenium自动化基础知识

    什么是自动化测试? 自动化测试分为:功能自动化和性能自动化 功能自动化即使用计算机通过编码的方式来替代手工测试,完成一些重复性比较高的测试,解放测试人员的测试压力.同时,如果系统有不份模块更改后,只要 ...

  10. [SQL] SQL 基础知识梳理(一)- 数据库与 SQL

    SQL 基础知识梳理(一)- 数据库与 SQL [博主]反骨仔 [原文地址]http://www.cnblogs.com/liqingwen/p/5902856.html 目录 What's 数据库 ...

随机推荐

  1. Solution -「CF 1349D」Slime and Biscuits

    \(\mathcal{Description}\)   Link.   有 \(n\) 堆饼干,一开始第 \(i\) 堆有 \(a_i\) 块.每次操作从所有饼干中随机一块,将其随机丢到另外一堆.求所 ...

  2. v77.01 鸿蒙内核源码分析(消息封装篇) | 剖析LiteIpc(上)进程通讯内容 | 新的一年祝大家生龙活虎 虎虎生威

    百篇博客分析|本篇为:(消息封装篇) | 剖析LiteIpc进程通讯内容 进程通讯相关篇为: v26.08 鸿蒙内核源码分析(自旋锁) | 当立贞节牌坊的好同志 v27.05 鸿蒙内核源码分析(互斥锁 ...

  3. 记录一次elasticsearch-5.6.4宕机排查经历

    犯罪现场~~ es: 三节点,配置相同 内存: 248G CPU: 没注意看 磁盘: 2T data: 380G左右 indices: 近9800条 在下才疏学浅,目前跟着大佬学习,这个问题还没解决, ...

  4. 麦克风阵列波束形成之DSB原理与实现

    语音识别有近场和远场之分,且很多场景下都会用到麦克风阵列(micphone array).所谓麦克风阵列是一组位于空间不同位置的麦克风按一定的形状规则布置形成的阵列,是对空间传播声音信号进行空间采样的 ...

  5. 实体类分层命名PO,VO,BO,DTO,POJO,DAO,DO

    一.Java中PO.DO.TO.DTO. VO. BO.POJO .DAO的概念 PO:persistant object持久对象 最形象的理解就是一个PO就是数据库中的一条记录.好处是可以把一条记录 ...

  6. zabbix监控多个nginx vhost网站状态码

    需求 假设一台服务器运行了N个vhost网站,如何确定在大流量并发时候找到是哪个网站的问题呢? 这似乎是每个运维都会遇到的问题,方法有很多比如:1.看nginx日志大小确定访问量.2.通过前端代理确定 ...

  7. Spring5基础

    基于Spring 5.2.6 版本. Spring概念 IOC容器 IOC底层原理的演进过程--本质就是为了高内聚,低耦合 在原始方式中,我们通过new创建对象来实现创建对象的逻辑,但是这样做当对象路 ...

  8. 传输层隧道技术之lcx内网端口转发

    传输层技术包括TCP隧道.UDP隧道和常规端口转发等.在渗透测试中,如果内网防火墙阻止了指定端口的访问,在获得目标机器的权限后,可以使用IPTABLES打开指定端口.如果内网中存在一系列防御系统,TC ...

  9. kali linux中ifconfig命令不能使用的解决办法

    1.安装net-tools,因ifconfig属于net-tools,输入命令:   sudo apt-get install net-tools 记住加上sudo哦!4647c21ef50df33a ...

  10. 【windows 访问控制】五、访问权限和访问掩码AcessMask

    访问掩码格式 所有安全对象都使用下图所示的访问掩码格式来安排其访问权限. 在这种格式中,低16位用于特定对象的访问权限,后8位用于标准访问权限,这些权限适用于大多数类型的对象,而4个高位用于指定通用访 ...