嵌入式八股文(四)FreeRTOS篇
系列文章目录
嵌入式八股文(一)C语言篇
嵌入式八股文(二)Linux应用篇
嵌入式八股文(三)Linux驱动篇
嵌入式八股文(四)FreeRTOS篇
文章目录
- 系列文章目录
- 一、内核
- 1.1 FreeRTOS启动流程
- 1.2 内核的基本组成及内核对象
- 1.3 FreeRTOS调度算法
- 1.4 哪些事件会触发任务调度
- 二、 内存管理
- 2.1 五种内存管理机制
- 2.2 如何避免内存泄露
- 三、同步及通讯
- 3.1. 优先级反转与解决方案
- 3.2. 临界区和临界资源
- 3.3. 二值信号量和互斥量的区别
- 四、其他
- 4.1 实时操作系统的优势及特点
- 4.2 FreeRTOS如何移植及对代码裁剪
- 4.3 空闲任务是否是必须的及其作用
一、内核
1.1 FreeRTOS启动流程
- 系统给上电后,首先会调用复位函数Reset_Handle,然后调用__main来初始化堆栈,最后跳转到C中的main函数中。
- main函数中会进行系统初始化(比如初始化系统时钟)和外设始化(比如初始化I2C、SPI等),并创建相应的任务,所有的任务都创建完成后则调用vTaskStartScheduler()函数来启动任务调度器,任务调度器是FreeRTOS的核心。
- 开启任务调度函数vTaskStartScheduler()中会执行如下操作:
① 创建空闲任务、定时器服务任务、关闭中断;
② 设置PendSV和Systick为最低优先级的中断,并且开启Systick中断;
③ 初始化一些全局变量,将变量xSchedulerRuning设置为True,表示调度器开始运行;
④ 初始化时钟节拍计数器、初始化临界区嵌套计数器,如果ARM内核支持FPU,还会使能FPU;
⑤ 最后调用vPortStartFirstTask()函数运行第一个任务(任务优先级最高的任务),这是一个汇编函数,通过SVC中断来启动第一个任务。(整个FreeRTOS中只在这里使用了一次SVC中断)
1.2 内核的基本组成及内核对象
FreeRTOS 内核的核心组成包括任务调度、内存管理、通信机制、中断处理和时间管理,其设计目标是为嵌入式系统提供高效、可靠的多任务环境。
1.3 FreeRTOS调度算法
FreeRTOS 支持两种调度模式:抢占式调度(默认,按优先级)和协作式调度(任务必须主动释放资源才会切换),而时间片轮转是抢占式调度中的一个子机制,通过时间片轮转(默认每个 tick 切换一次)实现分时共享 CPU。
时间片轮转调度在以下情况会发生任务切换
- 进程使用互斥锁(被动切换),互斥锁不可用时
- 进程主动休眠(主动切换)
- 进程被撤销(强制切换)
- 进程当前时间片使用完(强制切换)
1.4 哪些事件会触发任务调度
- 任务挂起或删除:当前任务调用vTaskSuspend()或vTaskDelete()后,会立即让出CPU,调度器选择其他就绪任务执行
- 任务优先级变化:当一个高优先级任务变为就绪状态(例如通过信号量释放、消息队列接收等),当前任务可能会被抢占。
- 系统节拍中断: 每次系统节拍(tick)中断都会触发调度器,检查是否有就绪任务需要切换(如时间片轮转的场合)
- 延时函数调用: 当任务调用vTaskDelay()或vTaskDelayUntil()时,任务进入阻塞状态,调度器会选择其他任务
二、 内存管理
2.1 五种内存管理机制
- heap_1.c:只分配不删除,只有pvPortMalloc,没有实现vPortFree;
- heap_2.c:最佳匹配算法,但不会合并相邻的空闲内存,碎片化严重;
- heap_3.c:使用标准C库里的malloc、free函数,configTOTAL_HEAP_SIZE不再起作用;
- heap_4.c:首次适应算法,会把相邻空闲内存合并为一个大的空闲内存,可以较少内存的碎片化;
- heap_5.c:分配内存、释放内存的算法跟Heap_4是一样的,不局限于管理一个大数组:它可以管理多块、分隔开的内存。
2.2 如何避免内存泄露
- 选择合适内存方案:1无法动态释放,3会带来碎片化,常用4或者5
- 正确释放动态分配的资源
- 避免创建未使用的任务及资源,启用时进行内存统计:采用vTaskList()
- 采用configASSERT()捕获
三、同步及通讯
3.1. 优先级反转与解决方案
优先级反转是指低优先级任务持有高优先级任务所需的资源(如互斥锁),导致高优先级任务被阻塞,而中优先级任务抢占 CPU,形成“反转”。
可以通过优先级继承(低优先级任务的优先级会被临时提升)或者使用消息队列和信号量来管理资源来解决这种情况。
3.2. 临界区和临界资源
临界区:指访问共享资源的代码段,必须保证一次只允许一个任务进入,以防止数据竞争。
临界资源:被多个任务共享且可能引发竞争的资源,访问它时需要保护机制(如互斥锁)。
3.3. 二值信号量和互斥量的区别
互斥量是二值信号量的的特殊形式(互斥信号量其实就是一个拥有优先级继承的二值信号量),其主要差距如下所示:
二值信号量 | 互斥量 | |
---|---|---|
应用场景 | 同步 | 保护共享资源 |
所有权 | any | 谁申请谁释放 |
优先级继承 | 不支持 | 支持(解决优先级反转问题) |
初始状态 | 通常初始化为 0(未被获取) | 初始化为 1(未被获取) |
四、其他
4.1 实时操作系统的优势及特点
在常用的前后台系统包含有一个后台任务和一个前台任务,整个系统结构简单,但对时间敏感的任务响应较差。而在实时操作系统(RTOS)中,多个任务可以被调度,系统通过任务调度器管理任务的执行顺序。RTOS可以保证高优先级任务得到及时的处理,更适合复杂系统中的实时响应需求。实时系统的基本特性如下所示:
确定性:系统能够在确定的时间内完成指定任务,确保任务按时完成。
可预测性:系统能够预测在特定条件下的行为,保证在任何情况下系统都能做出可预见的响应。
高可靠性:系统能够长时间运行且不出错,尤其在关键应用中,可靠性至关重要。
4.2 FreeRTOS如何移植及对代码裁剪
FreeRTOS的移植:
- 在源程序中创建freertos文件夹
- 在文件夹中创建src存放source的核心文件,就是.c文件
- 在文件夹中创建port存放MenMang内存管理文件和RVDS处理器架构相关代码
- 最后再将include头文件和FreeRTOSConfig.h配置文件移植进来即可
FreeRTOS的裁剪:
- task.c和list.c是必须的,其他的核心文件按需选择即可;
- RVDS中只留下我们使用到的芯片的即可,其他可以删除;
- 内存管理有5个文件,我们一般留heap_4.c,其他可以删除
4.3 空闲任务是否是必须的及其作用
FreeRTOS 的空闲任务(Idle Task) 是必须存在的,它是 FreeRTOS 调度器自动创建的一个默认任务(优先级最低,通常为 0)。它的核心作用是确保系统始终有一个可运行的任务,维持调度器的正常运转。以下是它的主要作用:
- 防止处理器空转:其他任务没有工作要执行时,就会执行空闲任务;
- 释放内存:调用 vTaskDelete() 删除任务时,实际内存释放由空闲任务完成;
- 执行低功耗模式;
- 执行钩子函数:空闲任务钩子函数必须是非阻塞的。