简介
1.信号量(Semaphore)
定义:信号量是一种轻量级的同步机制,用于控制对共享资源的访问或通知任务之间的状态变化。
类型:
类型 | 描述 |
---|---|
二值信号量(Binary Semaphore) | 最简单的信号量,只有两个状态:可用(1)和不可用(0)。常用于任务间的同步。 |
计数信号量(Counting Semaphore) | 可以有多个计数值,适用于资源池管理(如多个缓冲区)。 |
递归信号量(Recursive Semaphore) | 允许同一个任务多次获取同一信号量,适用于嵌套调用场景。 |
2.互斥量(Mutex)
定义:互斥量(Mutex)是一种特殊的信号量,用于保护共享资源,确保同一时间只有一个任务可以访问该资源。
特点:
独占性:同一时刻只能有一个任务持有互斥量。
优先级继承(可选):如果高优先级任务等待低优先级任务释放互斥量,系统会暂时提升低优先级任务的优先级,避免优先级倒置问题。
3.信号量与互斥量的作用
特性 | 信号量(Semaphore) | 互斥量(Mutex) |
---|---|---|
用途 | 同步、资源管理、事件通知 | 资源保护 |
是否支持递归 | 部分类型支持(如递归信号量) | 支持(递归互斥量) |
是否支持优先级继承 | 不支持 | 支持(可配置) |
操作方式 | 多个任务可以同时获取(计数信号量) | 仅允许一个任务获取 |
适合场景 | 任务间同步、资源池管理 | 保护共享资源 |
信号量(Semaphore)
1.常用 API 函数
函数 | 描述 |
---|---|
xSemaphoreCreateBinary() | 创建一个二值信号量。 |
xSemaphoreCreateCounting() | 创建一个计数信号量。 |
xSemaphoreCreateRecursiveMutex() | 创建一个递归互斥量(也可视为一种递归信号量)。 |
xSemaphoreTake() | 获取信号量(阻塞或非阻塞)。 |
xSemaphoreGive() | 释放信号量。 |
xSemaphoreTakeFromISR() | 在中断服务程序中获取信号量。 |
xSemaphoreGiveFromISR() | 在中断服务程序中释放信号量。 |
2.信号量的创建与使用
2.1二值信号量创建:
SemaphoreHandle_t xBinarySemaphore = xSemaphoreCreateBinary();
2.2计数信号量
SemaphoreHandle_t xCountingSemaphore = xSemaphoreCreateCounting(10, 0); // 初始值为0,最大值为10
2.3递归互斥量
SemaphoreHandle_t xRecursiveMutex = xSemaphoreCreateRecursiveMutex();
2.4获取信号量,阻塞式获取(等待信号量)
xSemaphoreTake(xSemaphore, portMAX_DELAY); // 永久等待
2.5获取信号量,非阻塞式获取
if (xSemaphoreTake(xSemaphore, 0) == pdTRUE) {
// 成功获取信号量
} else {
// 无法获取信号量
}
2.6释放二值信号量或计数信号量
xSemaphoreGive(xSemaphore);
2.7释放递归互斥量
xSemaphoreGiveRecursive(xRecursiveMutex);
2.8在中断服务程序中获取信号量
xSemaphoreTakeFromISR
(
SemaphoreHandle_t xSemaphore,
signed BaseType_t *pxHigherPriorityTaskWoken
)
2.9在中断服务程序中释放信号量
xSemaphoreGiveFromISR
(
SemaphoreHandle_t xSemaphore,
signed BaseType_t *pxHigherPriorityTaskWoken
)
互斥量(Mutex)
1.常用API函数
函数 | 描述 |
---|---|
xSemaphoreCreateMutex() | 创建一个互斥量。 |
xSemaphoreTake() | 获取互斥量(阻塞或非阻塞)。 |
xSemaphoreGive() | 释放互斥量。 |
xSemaphoreTakeFromISR() | 在中断服务程序中获取互斥量。 |
xSemaphoreGiveFromISR() | 在中断服务程序中释放互斥量。 |
2.互斥量的创建与使用
2.1互斥量的创建
SemaphoreHandle_t xMutex = xSemaphoreCreateMutex();
注意:在 FreeRTOS 中,建议使用 xSemaphoreCreateMutex()
而不是 xSemaphoreCreateBinary()
来创建互斥量,因为前者支持优先级继承,避免死锁问题。
2.2获取互斥量(Lock)
BaseType_t xResult = xSemaphoreTake(xMutex, portMAX_DELAY);
参数说明:
xMutex
:互斥量句柄。
portMAX_DELAY
:无限等待,直到获取到互斥量。
其他等待时间(如 pdMS_TO_TICKS(100)
)可设置为超时时间。
2.3释放互斥量(Unlock)
xSemaphoreGive(xMutex);
信号量与互斥量的区别
1.功能差异
功能 | 信号量 | 互斥量 |
---|---|---|
资源保护 | 不直接保护资源,而是用于任务间的同步或通信 | 专门用于保护共享资源(如全局变量、外设等),确保同一时间只有一个任务可以访问该资源 |
可重入性 | 通常不支持递归获取,因为它们主要用于同步而不是资源保护 | 支持递归获取(即同一个任务可以多次获取同一个互斥量,无需释放) |
优先级继承 | 没有优先级继承机制 | 如果一个低优先级任务持有互斥量,而高优先级任务等待该互斥量,那么低优先级任务会临时提升优先级以避免优先级倒置 |
计数值 | 可以设置一个计数值(例如二值信号量或计数型信号量),表示可用资源的数量 | 始终只能有一个可用资源(计数值为 1) |
2.使用场景对比
信号量适用场景:
- 通知任务事件发生(如中断完成、数据到达等)。
- 控制任务的执行顺序(如任务 A 等待任务 B 的信号)。
- 资源池管理(如缓冲区数量有限时)。
互斥量适用场景:
- 保护共享资源(如全局变量、硬件寄存器等)。
- 防止多任务同时访问共享资源导致的数据不一致问题。
- 递归访问共享资源(如嵌套函数调用中访问同一资源)。
示例代码
1.信号量示例
#include "FreeRTOS.h"
#include "semphr.h"
SemaphoreHandle_t xSemaphore;
void vTaskFunction(void *pvParameters) {
// 获取信号量
if (xSemaphoreTake(xSemaphore, portMAX_DELAY) == pdTRUE) {
// 执行操作
}
}
void vInterruptHandler(void) {
// 发送信号量
xSemaphoreGiveFromISR(xSemaphore, NULL);
}
2.互斥量示例
include "FreeRTOS.h"
#include "semphr.h"
SemaphoreHandle_t xMutex;
void vTaskFunction(void *pvParameters) {
// 获取互斥量
if (xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) {
// 访问共享资源
// ...
// 释放互斥量
xSemaphoreGive(xMutex);
}
}
常见问题与注意事项
1.死锁问题
- 1.1死锁的四个必要条件:
- 互斥(Mutual Exclusion):资源不能被同时访问
- 占有并等待(Hold and Wait):任务持有至少一个资源,并等待其他资源。
- 不可抢占(No Preemption):资源只能由持有它的任务释放。
- 循环等待(Circular Wait):存在一个任务链,每个任务都在等待下一个任务所持有的资源。
- 1.2引发死锁的常见情况:
- 多个互斥量的错误获取顺序
- 在中断服务程序中使用互斥量
- 不正确的超时设置
2.优先级翻转问题
- 优先级翻转是指在一个多任务系统中,一个高优先级的任务被低优先级的任务阻塞,从而无法执行其任务的情况。
FreeRTOS 提供了 优先级继承(Priority Inheritance) 机制来解决优先级翻转问题
3.资源释放与管理
- 3.1信号量的释放 应当始终在相同的任务或中断上下文中进行。
- 3.2避免资源泄漏:确保每次
xSemaphoreTake()
都有对应的xSemaphoreGive()
。 - 3.3合理设置超时时间:在调用
xSemaphoreTake()
时,设置合适的超时时间以防止任务长时间阻塞。