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

FreeRTOS深入理解

系列文章目录


文章目录

  • 系列文章目录
  • 内存管理方案
  • 任务调度
      • 任务状态的内部原理
      • 中断
      • 临界区保护
      • 任务句柄
      • 任务调度器
      • 通信方式
      • 优先级反转
      • 低功耗
      • FreeRTOS和Linux的区别
      • STM32的Flash和RAM


malloc申请的堆内存空间,同时会存储下申请的大小,free释放的时候,会导致堆碎片化,这个时候空闲内存肯定是通过链表连接在一起的

任务切换时,上下文保护,cpu寄存器只会压栈,以便后续恢复现场
PC(Program Counter 程序计数器):R15,存储下一个将要执行的指令的地址
SP(Stack Pointer 堆栈指针):R13,SP始终会指向栈的下一个空闲空间的地址
LR(Link Resigter 链接寄存器):R14,用于保存子程序或者是中断的返回地址
存储函数中定义的局部变量
保存函数调用时的地址,参数传递等信息
在这里插入图片描述
分静态分配和动态分配

// 静态定义任务栈数组和任务句柄
static StackType_t xTaskStack[ configMINIMAL_STACK_SIZE ];
static StaticTask_t xTaskBuffer;// 创建静态任务
TaskHandle_t xTaskCreateStatic(TaskFunction_t pxTaskCode,       // 任务函数const char * const pcName,       // 任务名称uint32_t ulStackDepth,           // 栈深度(单位:字,如32位系统为4字节/字)void * const pvParameters,       // 任务参数UBaseType_t uxPriority,          // 任务优先级StackType_t * const puxStackBuffer, // 栈数组StaticTask_t * const pxTaskBuffer // 任务控制块
);

动态创建自动从堆中分配栈空间

**// 创建动态任务
BaseType_t xTaskCreate(TaskFunction_t pxTaskCode,const char * const pcName,configSTACK_DEPTH_TYPE usStackDepth, // 栈深度(单位:字)void * const pvParameters,UBaseType_t uxPriority,TaskHandle_t * const pxCreatedTask
);**

堆总大小定义

#define configTOTAL_HEAP_SIZE (16 * 1024)  // 16KB堆空间

内存管理方案

???
FreeRTOSConfig.h 的 configUSE_MALLOC_FAILED_HOOK
方案 1(heap_1.c)
特点:仅支持内存分配(无释放),速度最快,无内存碎片。
适用场景:任务数量固定且无需删除的简单系统。
方案 2(heap_2.c)
特点:支持分配和释放,使用最佳匹配算法,但可能导致碎片化。
适用场景:任务创建和删除频繁,但栈大小相近的系统。
方案 3(heap_3.c)
特点:封装标准 C 库的malloc()和free(),线程安全。
适用场景:依赖标准库内存管理的系统。
方案 4(heap_4.c)
特点:支持合并相邻空闲块,减少碎片化,使用首次匹配算法。
适用场景:任务栈大小差异较大的复杂系统。
方案 5(heap_5.c)
特点:支持在不连续的内存区域(如 SRAM 和外部 RAM)分配堆。
适用场景:内存分布分散的系统

单片机的RAM,一部分用于编译时分配了全局变量和静态局部变量,剩下的才是堆和栈

任务调度

有三种方法:时间片轮转(同等优先级)、抢占式调度(高优先级抢占,响应快)、协程式调度(无抢占,低优先级任务不会被强制打断,适合共享资源无需锁的场景,变裸机开发?)

//抢占式+时间片轮转
#define configUSE_PREEMPTION     1  // 启用抢占式调度
#define configUSE_TIME_SLICING   1  // 启用时间片轮转(默认值)
#define configTICK_RATE_HZ     1000 // 设置时间片长度(如1ms)// 纯协作式调度
#define configUSE_PREEMPTION        0
#define configUSE_TIME_SLICING      0

任务状态:运行态、就绪态、阻塞态、挂起态
挂起类似暂停,需要vTaskSuspend进入挂起、调用vTaskResume来解挂

删除态官方文件没有,应该不算,删除对应创建

任务对应的协程,协程不能通信,用于RAM资源严重不足的情况,调用 xCoRoutineCreate() 即可创建协程。协程优先级和任务不混,两个同时存在时,任务优先级始终高于协程,所以只有在没有优先级高于空闲任务的任务可以执行时才会执行协程。

任务状态的内部原理

Task Control Block,TCB,任务控制块
包含了管理任务所需的关键信息:

任务状态,用于表明任务当前处于运行、就绪、阻塞或挂起等状态;
任务优先级,决定了任务在调度时的顺序;
任务栈指针,指向任务栈的顶部,用于保存任务上下文;
任务句柄,作为任务的唯一标识;
还有任务名称,方便调试和识别。
此外,TCB 中还可能包含任务的阻塞时间、等待事件的相关信息,以及指向链表节点的指针,用于将任务组织到不同优先级的就绪链表、阻塞链表或挂起链表中,从而实现操作系统对任务的高效管理和调度。

动态创建会分配两块内容,一块用于TCB、一块用于任务栈

任务调度机制核心依赖于任务链表(Task Lists),链表通过嵌入在 TCB 中的节点(ListItem_t)来管理任务。

时间片轮转的时候:

当调度器触发时间片轮转时:

  1. 定位当前任务优先级P:通过TCB获取当前任务优先级
  2. 访问优先级P对应的就绪链表:pxReadyTasksLists[P]
  3. 检查该优先级下的任务数:uxCurrentNumberOfTasks[P]
    • 若任务数=1:无需轮转,当前任务继续执行
    • 若任务数>1:执行时间片轮转
  4. 时间片计数器递减:
    • 每个任务运行时,优先级P的时间片计数器(xSchedulerRunning等相关变量)递减
    • 当计数器减为0时,触发轮转
  5. 链表遍历与任务切换:
    • 将当前任务从链表头部移除,插入到链表尾部
    • 取出链表新头部的任务作为下一个运行任务
    • 执行上下文切换(保存当前任务寄存器,恢复新任务状态)

PendSV与操作系统中的SVC协同工作。SVC不能挂起,它将立即被执行;而PendSV可以暂时挂起异常,对于操作系统来说这很有用,它可以等待一个重要的任务执行完毕后再处理该异常。
RTOS上下文切换在PendSv(pendable system call,可挂起系统调用)异常中进行,主要指的任务切换
原因:
1.PendSv优先级非常低,不影响中断执行
2.可挂起,触发延迟执行,确保不影响中断响应,中断完成后,再压栈任务的相关参数

Cortex-M架构中,中断参数的参数压栈和上下文保护是硬件自动完成

任务阻塞是指任务因等待某个事件(如延时、信号量、队列消息)而暂时无法执行的状态。当任务阻塞时,系统会将其从就绪链表移至阻塞链表,并在事件满足后再移回调就绪链表

中断

Cortex-M使用的是8位的寄存器来配置中断的优先级,在STM32中,只使用了高4位来配置中断优先级,所以最大只有16级。
中断优先级要在5-15之间,宏定义设置的

抢占优先级:高优先级可以打断低优先级的中断
子优先级:相同抢占优先级的两个中断同时发生的时候,子优先级数值小的先执行(数值小优先级高)(子优先级的中断之间不会抢占)
在这里插入图片描述
建议将所有优先级位指定为抢占优先级位,方便FreeRTOS管理(设置成分组4)

中断数值越小,优先级越大
任务的数值越大,优先级越大,最大优先级数程序里设置了,0-31

在这里插入图片描述

临界区保护

任务和中断里面都可以设置临界区保护,只是函数不一样
适用于:
1.外设初始化
2.操作系统的代码有很多不能被打断
3.用户自己的需求

任务句柄

任务句柄内容有,

任务调度器

osKernelStart()里面有开启任务调度器vTaskStartScheduler() 实现过程:
创建空闲任务
如果使能软件定时器,就创建定时器任务
关闭中断,防止调度器开启前收到中断干扰,运行第一个任务时会打开中断
初始化全局变量,将任务调度器标志设置为已运行
调用函数xPortStartScheduler()

通信方式

队列、信号量、互斥锁、任务通知、事件标志组

信号量有二值信号量和计数信号量(获取和释放操作)

优先级反转

优先级翻转在抢占式内核中是非常常见的,但是在实时操作系统中是不允许出现优先级翻转的,因为优先级翻转会破坏任务的预期顺序,可能会导致未知的严重后果。

互斥信号量也叫互斥锁,优先级继承:当一个互斥信号量正在被一个低优先级的任务持有时, 如果此时有个高优先级的任务也尝试获取这个互斥信号量,那么这个高优先级的任务就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级。
这样将优先级翻转的危害降到了最低,防止其他低优先级的任务一直执行,不

互斥锁不能用于中断,因为潜在死锁和休眠的风险,使用自旋锁可以
信号量只有非阻塞的能用,反正中断必须运行时间短

低功耗

FreeRTOS和Linux的区别

FreeRTOSLinux
RTOS内核大小极简Linux庞大
内存需求很小通常几十MB
启动时间ms启动时间秒级
确定实时性,在严格的时间约束内,可预测地完成特定任务的能力通用性、高吞吐量、多任务分时处理
开发工具轻量keil等丰富如GCC、GDB

STM32的Flash和RAM

SRAM是静态存储器,只要有电就能保持,所以适合高速缓存,读取后也不需要复写,KB-MB,L1、L2、L3、cache
DRAM是动态随机存取存储器,数据要周期性刷新才行,读取后要复写,但用到晶体管少,可以大量集成,用在主存储器,GB,内存条

srm32的flash就是ROM,(通常保存着text段、Code、Ro-data、Rw-data)
RAM(通常保存着堆、栈、bss段、data段、ZI-data、RW-data)

在这里插入图片描述
map文件会有
在这里插入图片描述
ZI-data:即 Zero Init-data, 0初始化的内存区的大小(该区域3个用途:0初始化的全局和静态变量+堆区+栈区)(RAM)。
RO-data:即 Read Only-data, 表示程序定义的常量,如 const 类型( FLASH)。
RW-data:即 Read Write-data, 非0初始化的全局和静态变量占用的RAM大小,同时还要占用等量的ROM大小用于存放这些非0变量的初值(FLASH+RAM)。

程序占用FLASH=Code + RO-data + RW-data 即map文件中ROM size
程序占用RAM = RW-data + ZI-data 即map文件中RW size

RW-data是非0初始化的数据,已初始化的数据需要被存储在掉电不会丢失的FLASH中,上电后会从FLASH搬移到RAM中。所以RW-data两边都占用一些

STM32F103C8T6是64KB Flash 20KB的RAM

http://www.dtcms.com/a/360347.html

相关文章:

  • 数据库索引abc,请问查询哪些字段能命中索引
  • 平滑滤波器(Smooth Filter)的MATLAB与Verilog仿真设计与实现
  • 关于Ctrl+a不能全选的问题
  • 封装哈希表
  • 机器视觉opencv教程(四):图像颜色识别与颜色替换
  • 【开题答辩全过程】以 基于SpringBoot的流浪猫狗领养系统为例,包含答辩的问题和答案
  • C语言中如何使用NULL
  • 【Linux】系统部分——ELF文件格式与动态库加载
  • Asible管理变量和事实和实施任务控制
  • 科学研究系统性思维的方法体系:研究设计相关模版
  • 【Unity3D实例-功能-切换武器】切换武器(一)动画配置
  • IAR 用JLINK 下载代码设置步骤
  • FLEXPART 拉格朗日粒子扩散模式建模技术及研究大气污染物源-汇关系中的实践
  • 订餐后台管理系统-day06菜品分类模块
  • 工业软件领域SAAS模式为何不能得到普及?
  • 上海交大具身导航中的感知智能、社会智能和运动智能全面综述
  • 网络端口与服务对应表 - 白帽子安全参考指南
  • Android开发-设计规范
  • 安装proteus,并实现stm32仿真
  • 当 AI 开始 “筛选” 信息:算法偏见会加剧认知鸿沟吗?如何构建公平的 AI 生态?
  • 深入解析 Oracle 并发与锁机制:高并发环境下的数据一致性之道
  • Log File Sync等待事件分析
  • linux日志同步
  • strtok()字符串分隔函数
  • OpenStack 01:介绍
  • Batch Normalization 批归一化
  • 实现自己的AI视频监控系统-第三章-信息的推送与共享1
  • AI辅助编程日记和chat历史开源Series 1:VSCode + GitHub Copilot 自动下载及安装软件
  • 大模型训练全流程
  • 在deepseek v3.1上加自信度参数的外挂方案,plugin,朝向一步一步