文章目录
- 一、同步和互斥
-
- [1.1 同步](#1.1 同步)
-
- 同步的核心场景与目标
- FreeRTOS中常用的同步机制
-
- [1. 二进制信号量(Binary Semaphore)](#1. 二进制信号量(Binary Semaphore))
- [2. 计数信号量(Counting Semaphore)](#2. 计数信号量(Counting Semaphore))
- [3. 事件标志组(Event Groups)](#3. 事件标志组(Event Groups))
- [4. 任务通知(Task Notifications)](#4. 任务通知(Task Notifications))
- [5. 消息队列(Message Queues)](#5. 消息队列(Message Queues))
- 同步与互斥的区别
- 总结
- [1.2 互斥](#1.2 互斥)
一、同步和互斥
1.1 同步
在FreeRTOS中,同步(Synchronization) 指的是多个任务之间(或任务与中断之间)为了协调执行节奏、共享事件或传递状态而进行的时序协作机制。其核心目的是确保任务按照预期的逻辑顺序执行,避免因执行时机错乱导致的数据错误、资源冲突或功能失效。
同步的核心场景与目标
在实时操作系统(RTOS)中,多任务并发执行时,往往需要通过同步解决以下问题:
- "等待事件":一个任务需要等待另一个任务(或中断)完成某个操作后才能继续执行(例如,传感器采集任务完成后,数据处理任务才能启动)。
- "步调一致":多个任务需按照特定顺序协同工作(例如,A任务生成数据→B任务处理数据→C任务存储数据,需严格按A→B→C的顺序执行)。
- "资源有序访问":当多个任务共享有限资源(如缓冲区、外设)时,通过同步确保资源使用的时序合理性(区别于"互斥",同步更侧重"顺序"而非"独占")。
FreeRTOS中常用的同步机制
FreeRTOS提供了多种同步工具,每种工具适用于不同场景:
1. 二进制信号量(Binary Semaphore)
-
原理 :类似"开关",只有两种状态(0或1),通常用于单次事件同步。
-
场景:
- 中断服务程序(ISR)通知任务处理事件(例如,串口接收数据后,ISR释放信号量,任务获取信号量后处理数据)。
- 任务A完成初始化后,释放信号量允许任务B启动。
c// 示例:ISR中释放信号量,任务中等待信号量 void UART_ISR(void) { xSemaphoreGiveFromISR(xBinarySemaphore, &xHigherPriorityTaskWoken); // ISR释放 } void vDataProcessTask(void *pvParam) { while(1) { xSemaphoreTake(xBinarySemaphore, portMAX_DELAY); // 等待信号量 // 处理数据 } }
2. 计数信号量(Counting Semaphore)
- 原理 :信号量值可在0到最大值之间变化,用于统计可用资源数量或事件次数。
- 场景 :
- 管理资源池(如5个缓存区,任务获取信号量使用缓存,释放后信号量值加1)。
- 累计事件次数(如计数按键按下次数,任务批量处理)。
3. 事件标志组(Event Groups)
-
原理:通过一个32位整数的不同位表示不同事件,任务可等待"任意一个事件"或"所有事件"发生。
-
场景:
- 任务等待多个事件中的一个(如"传感器A触发"或"传感器B触发")。
- 任务等待多个事件同时发生(如"温度达标"且"湿度达标"后启动风扇)。
c// 示例:等待两个事件同时发生 EventBits_t xBits = xEventGroupWaitBits( xEventGroup, // 事件组 (1 << 0) | (1 << 1), // 等待事件0和事件1 pdTRUE, // 获取后清除标志位 pdTRUE, // 需两个事件同时发生 portMAX_DELAY // 无限等待 ); if( (xBits & ((1 << 0) | (1 << 1))) == ((1 << 0) | (1 << 1)) ) { // 两个事件均发生,执行操作 }
4. 任务通知(Task Notifications)
- 原理:通过直接向任务发送通知(替代信号量/事件组),效率更高(无需创建额外内核对象)。
- 场景:轻量级同步(如替代二进制信号量,减少内存开销)。
5. 消息队列(Message Queues)
- 原理:通过队列传递数据,同时隐含同步功能(发送方和接收方需等待队列有空间/有数据)。
- 场景:同步+数据传递(如任务A发送数据到队列,任务B等待队列有数据后处理)。
同步与互斥的区别
需注意,同步与"互斥(Mutex)"是不同概念:
- 同步:解决"时序协调"问题,确保任务按逻辑顺序执行(如"先生产后消费")。
- 互斥:解决"资源独占"问题,防止多个任务同时访问共享资源(如用互斥锁保护全局变量)。
例如:两个任务共享打印机时,互斥 确保同一时间只有一个任务使用打印机;而同步可能确保"任务A打印完文档1后,任务B才能打印文档2"。
总结
FreeRTOS中的同步是多任务协作的核心机制,通过信号量、事件标志组等工具,确保任务(或中断)按照预期的时序执行,避免因并发导致的逻辑混乱。实际开发中,需根据具体场景(如事件数量、是否传递数据、资源类型)选择合适的同步工具,以保证系统的实时性和可靠性。
1.2 互斥
在FreeRTOS(或更广泛的操作系统领域)中,互斥(Mutual Exclusion,简称"互斥") 是一种解决多个任务对共享资源竞争访问 的机制,核心目标是确保同一时间只有一个任务能访问特定的共享资源,从而避免因并发操作导致的数据错乱、逻辑冲突或系统异常。
互斥的核心问题:资源竞争与数据不一致
在多任务系统中,多个任务可能同时需要访问"共享资源"(如全局变量、硬件外设、缓冲区等)。如果缺乏协调,可能出现以下问题:
- 例如,任务A和任务B同时向同一个串口发送数据,最终输出的内容会变成两者的混合乱码;
- 任务A读取全局变量
count=5并执行count++(计划改为6),但执行中途被任务B抢占,任务B读取count=5并改为6,随后任务A恢复执行,继续将count改为6------最终结果应为7,却错误地变成6。
这种因"同时访问"导致的错误,称为资源竞争(Race Condition)。互斥机制的作用就是通过"独占访问"避免此类问题。
FreeRTOS中互斥的实现:互斥锁(Mutex)
FreeRTOS通过互斥锁(Mutex) 实现互斥功能,其本质是一种特殊的信号量,具有以下特性:
-
独占性 :
互斥锁只有"持有"和"释放"两种状态。当一个任务获取(take)互斥锁后,其他任务必须等待(block),直到该任务释放(give)互斥锁,才能有机会获取。
c// 示例:创建互斥锁并使用 SemaphoreHandle_t xMutex; void vInitTask(void *pvParam) { xMutex = xSemaphoreCreateMutex(); // 创建互斥锁 // ... 创建其他任务 } void vTaskA(void *pvParam) { while(1) { // 尝试获取互斥锁,最多等待100ms if(xSemaphoreTake(xMutex, 100) == pdTRUE) { // 成功获取锁,安全访问共享资源(如全局变量、串口) shared_data = 123; printf("TaskA: %d\n", shared_data); xSemaphoreGive(xMutex); // 释放锁 } vTaskDelay(100); } } void vTaskB(void *pvParam) { while(1) { // 尝试获取互斥锁,无限等待 if(xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) { // 安全访问共享资源 shared_data = 456; printf("TaskB: %d\n", shared_data); xSemaphoreGive(xMutex); // 释放锁 } vTaskDelay(150); } } -
优先级继承(Priority Inheritance) :
这是FreeRTOS互斥锁的关键特性,用于解决"优先级反转"问题:
- 优先级反转:低优先级任务持有互斥锁时,中等优先级任务抢占CPU,导致高优先级任务(等待该锁)被长时间阻塞,违背实时性要求。
- 优先级继承:当高优先级任务等待低优先级任务持有的互斥锁时,低优先级任务的优先级会临时提升到与高优先级任务相同,直到释放锁,从而避免被中等优先级任务抢占,保证高优先级任务的响应速度。
-
所有权 :
互斥锁具有"所有权"概念------只有获取锁的任务才能释放锁(信号量则无此限制)。这避免了其他任务误释放锁导致的混乱。
互斥与同步的核心区别
| 维度 | 互斥(Mutex) | 同步(如信号量、事件组) |
|---|---|---|
| 核心目标 | 解决"资源独占"问题,防止竞争访问 | 解决"时序协调"问题,确保任务按逻辑顺序执行 |
| 关注点 | 资源的"安全性"(数据一致、操作正确) | 任务的"有序性"(先做什么,后做什么) |
| 典型场景 | 多个任务访问同一串口、全局变量、硬件外设等 | 任务A等待任务B完成初始化、中断通知任务处理数据等 |
| 关键特性 | 优先级继承、所有权 | 信号量计数、事件标志位组合等 |
互斥的使用原则
- 最小持有时间:获取互斥锁后,应尽快完成对共享资源的操作并释放锁,减少其他任务的等待时间。
- 避免嵌套死锁:不要在持有一个互斥锁时尝试获取另一个互斥锁(或同一锁),否则可能导致任务相互等待(死锁)。
- 适配场景:仅对"需要独占访问的共享资源"使用互斥锁,无需保护的资源(如只读变量)无需加锁,避免过度消耗系统资源。
总结
互斥是FreeRTOS中保障共享资源安全访问的核心机制,通过互斥锁实现"同一时间仅一个任务访问资源",并通过优先级继承解决实时性问题。它与同步机制(如信号量)相辅相成:同步确保任务"按顺序执行",互斥确保资源"被安全访问",共同支撑多任务系统的稳定运行。