目录
[互斥量 ------ " 谁上锁,就只能由谁开锁"](#互斥量 —— “ 谁上锁,就只能由谁开锁”)
[二、互斥量的基本使用 获取(Take)和释放(Give)](#二、互斥量的基本使用 获取(Take)和释放(Give))
[2、释放互斥量(归还资源)------ 必须由获取它的任务释放](#2、释放互斥量(归还资源)—— 必须由获取它的任务释放)
互斥量 ------ " 谁上锁,就只能由谁开锁"
对变量的非原子化访问:
修改变量、设置结构体、在16位的机器上写32位的变量,这些操作都是非原子的。也就是它们的操作过程都可能被打断,如果被打断的过程有其他任务来操作这些变量,就可能导致冲突。
函数重入:
"可重入的函数"是指:多个任务同时调用它、任务和中断同时调用它,函数的运行也是安全的。可重入的函数也被称为"线程安全"(thread safe)。
每个任务都维持自己的栈、自己的CPU寄存器,如果一个函数只使用局部变量,那么它就是线程安全的。
函数中一旦使用了全局变量、静态变量、其他外设,它就不是"可重入的",如果该函数正在被调用,就必须阻止其他任务、中断再次调用它。
上述问题的解决方法是:任务A访问这些全局变量、函数代码时,独占它,就是上个锁。这些全局变量、函数代码必须被独占地使用,它们被称为临界资源。
可以把函数想象成一间厨房:
- 可重入函数:每个使用者(任务 / 中断)都自带食材(局部变量,存在自己的栈里),用完厨房就走,互不干扰,哪怕有人中途进来用厨房也没事。
- 不可重入函数:厨房共用一桶油(全局 / 静态变量),如果 A 正在倒油时被 B 打断,B 把油倒走了,A 回来继续倒就会出错 ------ 这就是 "重复进入" 导致的问题。
"重入" 不是简单的 "重复进入",而是并发场景下的重复调用。"可重入" 的关键是函数不依赖共享资源,能安全应对这种并发调用;"不可重入" 则会因共享资源竞争而失效。
具体的可以参看韦东山老师:
一、互斥量的创建过程
1、包含头文件
#include "semphr.h" // 互斥量属于信号量范畴,需包含此头文件
2、定义互斥量句柄
SemaphoreHandle_t xMutex; // 互斥量句柄
3、调用创建函数
返回值 :成功创建返回互斥量句柄,失败(如内存不足)返回NULL。
xMutex = xSemaphoreCreateMutex();
4、检查创建结果
if(xMutex == NULL) {
// 互斥量创建失败,需错误处理(如提示、重启)
}
二、互斥量的基本使用 获取(Take)和释放(Give)
遵循 "先获取、后使用、用完释放" 的原则,用于保护共享资源的临界区。
1、获取互斥量(占用资源)
通过**xSemaphoreTake()**获取互斥量,确保当前任务独占共享资源:(放任务中)
BaseType_t xStatus;
// 获取互斥量:永久阻塞(portMAX_DELAY),直到获取成功
xStatus = xSemaphoreTake(
xMutex, // 目标互斥量句柄
portMAX_DELAY // 阻塞超时时间(可设为具体tick数,0为非阻塞)
);
if(xStatus == pdPASS) {
// 获取成功,可进入临界区访问共享资源
}
2、释放互斥量(归还资源)------ 必须由获取它的任务释放
通过**xSemaphoreGive()**释放互斥量,允许其他任务获取并使用资源:(放任务中)
// 释放互斥量(必须由获取它的任务释放)
xSemaphoreGive(xMutex);
三、使用规范和特性
临界区保护原则
- 访问共享资源(如串口、LCD、全局变量) 的代码必须放在**"获取互斥量后、释放互斥量前" 的临界区内**;
- 临界区代码应尽量简短,避免长时间占用互斥量导致其他任务阻塞。
特性
- 所有权 :只有获取互斥量的任务能释放它(二进制信号量无此限制);
- 优先级继承:若低优先级任务占用互斥量,高优先级任务等待时,低优先级任务会临时继承高优先级,避免 "优先级反转";
- 中断禁用:互斥量不能在中断服务函数中使用(二进制信号量可通过FromISR函数在中断中操作)。
四、保护串口打印的例子
#include "semphr.h"
// 1. 创建互斥量
SemaphoreHandle_t xUART_Mutex = xSemaphoreCreateMutex();
// 2. 任务访问串口的逻辑
void Task(void *pvParameters) {
for(;;) {
// a. 获取互斥量
if(xSemaphoreTake(xUART_Mutex, portMAX_DELAY) == pdPASS) {
// b. 临界区:安全使用串口
HAL_UART_Transmit(&huart1, data, len, 100);
// c. 释放互斥量
xSemaphoreGive(xUART_Mutex);
}
vTaskDelay(100);
}
}