FREERTOS_互斥量_创建和使用

目录

[互斥量 ------ " 谁上锁,就只能由谁开锁"](#互斥量 —— “ 谁上锁,就只能由谁开锁”)

对变量的非原子化访问:

函数重入:

一、互斥量的创建过程

1、包含头文件

2、定义互斥量句柄

3、调用创建函数

4、检查创建结果

[二、互斥量的基本使用 获取(Take)和释放(Give)](#二、互斥量的基本使用 获取(Take)和释放(Give))

1、获取互斥量(占用资源)

[2、释放互斥量(归还资源)------ 必须由获取它的任务释放](#2、释放互斥量(归还资源)—— 必须由获取它的任务释放)

三、使用规范和特性

临界区保护原则

特性

四、保护串口打印的例子


互斥量 ------ " 谁上锁,就只能由谁开锁"

对变量的非原子化访问:

修改变量、设置结构体、在16位的机器上写32位的变量,这些操作都是非原子的。也就是它们的操作过程都可能被打断,如果被打断的过程有其他任务来操作这些变量,就可能导致冲突。

函数重入:

"可重入的函数"是指:多个任务同时调用它、任务和中断同时调用它,函数的运行也是安全的。可重入的函数也被称为"线程安全"(thread safe)。

每个任务都维持自己的栈、自己的CPU寄存器,如果一个函数只使用局部变量,那么它就是线程安全的。

函数中一旦使用了全局变量、静态变量、其他外设,它就不是"可重入的",如果该函数正在被调用,就必须阻止其他任务、中断再次调用它。

上述问题的解决方法是:任务A访问这些全局变量、函数代码时,独占它,就是上个锁。这些全局变量、函数代码必须被独占地使用,它们被称为临界资源。
可以把函数想象成一间厨房:

  • 可重入函数:每个使用者(任务 / 中断)都自带食材(局部变量,存在自己的栈里),用完厨房就走,互不干扰,哪怕有人中途进来用厨房也没事。
  • 不可重入函数:厨房共用一桶油(全局 / 静态变量),如果 A 正在倒油时被 B 打断,B 把油倒走了,A 回来继续倒就会出错 ------ 这就是 "重复进入" 导致的问题。

"重入" 不是简单的 "重复进入",而是并发场景下的重复调用。"可重入" 的关键是函数不依赖共享资源,能安全应对这种并发调用;"不可重入" 则会因共享资源竞争而失效。

具体的可以参看韦东山老师:

​​​​​​​​​​​​​​https://rtos.100ask.net/zh/FreeRTOS/DShanMCU-F103/chapter13.html#_13-1-%E4%BA%92%E6%96%A5%E9%87%8F%E7%9A%84%E4%BD%BF%E7%94%A8%E5%9C%BA%E5%90%88

一、互斥量的创建过程

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);
  }
}
相关推荐
一起养小猫1 小时前
《Java数据结构与算法》第三篇(下)队列全解析:从基础概念到高级应用
java·开发语言·数据结构
pale_moonlight1 小时前
十、 Scala 应用实践 (上)
大数据·开发语言·scala
6***v4171 小时前
搭建Golang gRPC环境:protoc、protoc-gen-go 和 protoc-gen-go-grpc 工具安装教程
开发语言·后端·golang
1***s6321 小时前
Rust在WebAssembly中的应用实践
开发语言·rust·wasm
水痕011 小时前
go使用cobra来启动项目
开发语言·后端·golang
czhaii2 小时前
单片机RTOS“实时响应时间”的定义与测量方法
单片机
scixing2 小时前
函数式编程 第八讲 循环者,递归也
开发语言·c#
2501_941879812 小时前
Python在微服务高并发异步API网关请求处理与智能路由架构中的实践
java·开发语言
闻缺陷则喜何志丹2 小时前
【SOSDP模板 容斥原理 逆向思考】3757. 有效子序列的数量|分数未知
c++·算法·力扣·容斥原理·sosdp·逆向思考