ESP32学习笔记_FreeRTOS(5)——Mutex

news/2025/1/16 10:30:05/

摘要(From AI):
这篇博客内容围绕 FreeRTOS 中的**互斥量(Mutex)递归互斥量(Recursive Mutex)**的使用进行了详细的介绍。整体结构清晰,涵盖了互斥量的基本概念、使用方式以及与其他同步机制(如二进制信号量)的比较,还提供了两段示例代码,演示了互斥量和递归互斥量在任务同步中的应用

前言:本文档是本人在依照B站UP:Michael_ee的视频教程进行学习时所做的学习笔记,可能存在疏漏和错误,如有发现,望指正。

文章目录

    • Mutex
      • xSemaphoreCreateMutex()
      • Example Code:Mutex Synchronization with Task Priorities in FreeRTOS
    • Recursive Mutex
      • xSemaphoreCreateRecursiveMutex()
      • xSemaphoreTakeRecursive()
      • xSemaphoreGiveRecursive()
      • Example Code:Recursive Mutex Synchronization Between Tasks in FreeRTOS

参考资料
Michael_ee 视频教程
freeRTOS官网
espressif 在线文档


Mutex

当一个任务持有互斥量时,如果另一个更高优先级的任务尝试获取同一个互斥量,持有该互斥量的任务的优先级会被提升到另一个试图获得当前互斥量的任务的优先级,以便使高优先级任务能够获取该互斥量。这种优先级提升被称为“优先级继承”,当互斥量被释放时,任务会恢复到原来的优先级

获取互斥量的任务必须始终归还互斥量,否则其他任务将无法获取该互斥量

和二进制变量的主要区别:

二进制信号量用于同步时,获取信号量(“take”)后,不需要再“归还”它。任务同步的实现是通过一个任务或中断“给予”信号量,另一个任务“获取”信号量

如果一个低优先级任务获取了二进制信号量,那么高优先级任务只能等待

互斥量则会使用优先级继承来解决这种情况,确保低优先级任务能够尽快释放互斥量(让当前任务尽快完成)

xSemaphoreCreateMutex()

创建互斥锁类型信号量,并返回可引用互斥锁的句柄

每个互斥类型的信号量都需要少量的RAM来保存信号量的状态。如果使用xSemaphoreCreateMutex()创建互斥锁,则需要内存从FreeRTOS堆中自动分配

#include “FreeRTOS.h”
#include “semphr.h”SemaphoreHandle_t xSemaphoreCreateMutex( void );

返回值

SemaphoreHandle_t​成功创建信号量,返回值是一个句柄,通过它可以引用创建的信号量

Others​如果由于没有足够的堆内存供FreeRTOS分配,信号量数据结构而无法创建信号量

RTOS_49">Example Code:Mutex Synchronization with Task Priorities in FreeRTOS

Task1 获取到互斥量后,Task2 进入死循环,由于 Task2 优先级比 Task1 高,此时 Task1 无法运行;一段时间后 Task3 尝试获取互斥量,但此时互斥量还在 Task1,因此 Task1 的优先级被调整至和 Task3 同优先级,比 Task2 高,可以继续运行;当 Task1 运行完毕释放互斥量时,Task3 获取互斥量,此时 Task1 被 Task2 卡住,无法再运行

有关xSemaphoreGive()的用法,详见ESP32学习笔记_FreeRTOS(4)——Semaphore
有关任务优先级,详见ESP32学习笔记_FreeRTOS(1)——Task的创建和使用

由于在ESP32ESP32-S3 等双核 MCU 上,FreeRTOS对任务进行双核调度,此时若 Task1 和 Task2 分别处于不同的核心,Task2 无法卡住 Task1 ,需要将所有任务创建在同一个核心上才能实现目标现象

虽然 Task2 会因为出发看门狗被重启,但是不影响本示例代码进行的实验

#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"#include "freeRTOS/semphr.h"SemaphoreHandle_t mutexHandle; // 创建一个互斥信号量句柄void Task1(void *pvParam)
{BaseType_t Status;while (true){printf("Task1 is running\n");Status = xSemaphoreTake(mutexHandle, 1000);if (Status == pdPASS){printf("Task1 get the mutex\n");for (int i = 0; i < 50; i++){printf("i in task1: %d\n", i);vTaskDelay(pdMS_TO_TICKS(1000));}xSemaphoreGive(mutexHandle);vTaskDelay(pdMS_TO_TICKS(5000));}else{printf("Task1 failed to get the mutex\n");vTaskDelay(pdMS_TO_TICKS(1000));}}
}void Task2(void *pvParam)
{printf("Task2 is running\n");vTaskDelay(pdMS_TO_TICKS(1000)); // 给 Task1 一些时间来获取互斥信号量while (true){; // 任务 2 会直接把整个程序卡住,只有比它优先级高的任务才能执行// vTaskDelay(pdMS_TO_TICKS(1000));}
}void Task3(void *pvParam)
{BaseType_t Status;printf("Task3 is running\n");vTaskDelay(pdMS_TO_TICKS(1000));while (true){Status = xSemaphoreTake(mutexHandle, 1000); // Task3 尝试获取互斥信号量// 将会失败,因为 Task1 已经获取了互斥信号量// 将 Task1 的优先级升高至 Task3 的优先级// 此时 Task1 继续运行if (Status == pdPASS){printf("Task3 get the mutex\n");for (int i = 0; i < 10; i++){printf("i in task3: %d\n", i);vTaskDelay(pdMS_TO_TICKS(1000));}xSemaphoreGive(mutexHandle);vTaskDelay(pdMS_TO_TICKS(5000));}else{printf("Task3 failed to get the mutex\n");vTaskDelay(pdMS_TO_TICKS(1000));}}
}void app_main(void)
{mutexHandle = xSemaphoreCreateMutex(); // 创建一个互斥信号量vTaskSuspendAll(); // 挂起任务调度xTaskCreatePinnedToCore(Task1, "Task1", 1024 * 5, NULL, 1, NULL, 0); // 由于 ESP32-S3 的双核调度,需要将所有任务创建在同一个核心上才能实现目标现象xTaskCreatePinnedToCore(Task2, "Task2", 1024 * 5, NULL, 2, NULL, 0);xTaskCreatePinnedToCore(Task3, "Task3", 1024 * 5, NULL, 3, NULL, 0);xTaskResumeAll(); // 恢复任务调度
}

Recursive Mutex

递归互斥锁是指在调用时,已经获取了当前互斥锁的任务可以继续多次获取互斥锁用于处理不同数据(如占用了一个资源后接着占用下一个资源,使用普通的二进制变量或互斥锁需要通过创建多个变量来实现,而使用递归互斥锁则只需再获取一次即可,释放时也只需释放相同的次数)

xSemaphoreCreateRecursiveMutex()

创建递归互斥锁类型的信号量,并返回可引用递归互斥锁的句柄

#include “FreeRTOS.h”
#include “semphr.h”SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void );

返回值

SemaphoreHandle_t​创建互斥锁成功,返回值是一个句柄,通过它可以引用创建的互斥锁

Others​如果由于没有足够的堆内存供FreeRTOS分配,信号量数据结构而无法创建信号量

xSemaphoreTakeRecursive()

获取一个递归互斥锁类型的信号量,该信号量之前已经使用xSemaphoreCreateRecursiveMutex()创建

#include “FreeRTOS.h”
#include “semphr.h”BaseType_t xSemaphoreTakeRecursive( SemaphoreHandle_t xMutex, TickType_t xTicksToWait );

参数

xMutex​要获取的信号量

xTicksToWait​任务等待信号量可用的最大时间(以 FreeRTOS 系统时钟节拍为单位)

返回值

  • pdPASS​信号量获取成功
  • pdFAIL​信号量获取失败

xSemaphoreGiveRecursive()

释放一个递归互斥锁类型的信号量

#include “FreeRTOS.h”
#include “semphr.h”BaseType_t xSemaphoreGiveRecursive( SemaphoreHandle_t xMutex );

参数

xMutex​要释放的信号量

xTicksToWait​任务等待信号量可用的最大时间(以 FreeRTOS 系统时钟节拍为单位)

返回值

  • pdPASS​信号量释放成功
  • pdFAIL​信号量释放失败

RTOS_233">Example Code:Recursive Mutex Synchronization Between Tasks in FreeRTOS

#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"#include "freeRTOS/semphr.h"SemaphoreHandle_t mutexHandle; // 创建一个互斥信号量句柄void Task1(void *pvParam)
{printf("Task1 is running\n");vTaskDelay(pdMS_TO_TICKS(1000));while (true){printf("A new loop for task1\n");printf("Task1 is running\n");xSemaphoreTakeRecursive(mutexHandle, portMAX_DELAY);printf("Task1 get A\n");for (int i = 0; i < 10; i++){printf("i for A in task1: %d\n", i);vTaskDelay(pdMS_TO_TICKS(1000));}xSemaphoreTakeRecursive(mutexHandle, portMAX_DELAY);printf("Task1 get B\n");for (int i = 0; i < 10; i++){printf("i for B in task1: %d\n", i);vTaskDelay(pdMS_TO_TICKS(1000));}printf("Task1 release B\n");xSemaphoreGiveRecursive(mutexHandle);vTaskDelay(pdMS_TO_TICKS(3000));printf("Task1 release A\n");xSemaphoreGiveRecursive(mutexHandle);vTaskDelay(pdMS_TO_TICKS(3000));// 只有当 Task1 连续释放两次信号量的时候,Task2 才能获取到信号量}
}void Task2(void *pvParam)
{printf("Task2 is running\n");vTaskDelay(pdMS_TO_TICKS(1000));while (true){printf("A new loop for task2\n");xSemaphoreTakeRecursive(mutexHandle, portMAX_DELAY);printf("Task2 get A\n");for (int i = 0; i < 10; i++){printf("i for A in task2: %d\n", i);vTaskDelay(pdMS_TO_TICKS(1000));}printf("Task2 release A\n");xSemaphoreGiveRecursive(mutexHandle);vTaskDelay(pdMS_TO_TICKS(3000));}
}void app_main(void)
{mutexHandle = xSemaphoreCreateRecursiveMutex(); // 创建一个递归互斥信号量vTaskSuspendAll();xTaskCreatePinnedToCore(Task1, "Task1", 1024 * 5, NULL, 1, NULL, 0);xTaskCreatePinnedToCore(Task2, "Task2", 1024 * 5, NULL, 2, NULL, 0);xTaskResumeAll();
}

http://www.ppmy.cn/news/1563569.html

相关文章

Django缓存系统详解:使用Redis提升应用性能

1. 引言 在现代Web开发中,性能优化是一个永恒的主题。随着用户数量的增加和功能的复杂化,如何保持应用的高效运行成为了开发者面临的主要挑战之一。Django,作为一个成熟的Web框架,提供了强大的缓存系统来应对这一挑战。本文将深入探讨Django缓存系统,特别关注如何使用Red…

安装本地测试安装apache-doris

一、安装前规划 我的服务器是三台麒麟服务器,2台跑不起来,这是我本地的,内存分配的也不多。 fe192.168.1.13 主数据库端口9030访问 8Gbe192.168.1.13内存4G 硬盘50be192.168.1.14内存4G 硬盘50be192.168.1.12内存4G 硬盘5013同时安装的fe和be 。 原理:192.168.1.13 服…

2025windows环境下安装RabbitMQ

官网下载地址&#xff1a; Installing on Windows | RabbitMQ下载速度挺快的加速推荐 &#xff1a; BitComet(比特彗星) 官网下载 &#xff1a; 先完成 exe 的下载&#xff0c;参考对照关系&#xff0c;下载对应Erlang 参考关系对照表完成下载 &#xff1a; OTP Versions Tre…

docker-compose和docker仓库

一、docker-compose 1.概述 docker-compose是一个自动编排工具&#xff0c;可以根据dockerfile自动化部署docker容器。 主要功能 配置定义 使用YAML文件&#xff08;通常命名为docker - compose.yml&#xff09;来描述应用程序的服务、网络和卷等配置。 容器编排 可以同时…

Bash语言的多线程编程

Bash语言的多线程编程 引言 在现代的计算环境中&#xff0c;随着多核处理器的广泛应用&#xff0c;多线程编程逐渐成为提高程序执行效率的重要方式。尽管Bash并不是一种传统意义上的多线程编程语言&#xff0c;但通过合理的设计和技巧&#xff0c;我们仍然可以在Bash中实现并…

hutool-http实现离线爬虫

文章目录 1.数据爬取流程2.离线爬虫(Hutool-http实现)1.获取数据2.数据清洗3.为什么有各种类型的强转4.数据入库 3.测试完整代码 1.数据爬取流程 1.分析数据源(怎么获取) 2.拿到数据后怎么处理 3.写入数据库存储 2.离线爬虫(Hutool-http实现) 1.获取数据 这里返回的是jso…

掌握 React 关键:理解 super () 和 super (props) 的不同应用

在 React 中&#xff0c;super() 和 super(props) 都与 React 类组件的构造函数&#xff08;constructor&#xff09;以及继承有关。为了理解它们之间的区别&#xff0c;我们需要了解 JavaScript 类继承机制以及 React 类组件的工作原理。 1. super() 与 super(props) 的区别 …

设置virtualBox7.0.12 ubuntu24.10 和 windows之间双向复制共享剪贴板

虚拟机配置如下&#xff1a; 在Ubuntu终端输入以下指令&#xff1a;(如果虚拟机重启之后剪贴板不共用了&#xff0c;再次执行第二条指令启动服务就可以解决&#xff0c;Ubuntu终端中复制有时需要使用右键->复制才会到剪贴板上) sudo apt install virtualbox-guest-x11 sudo…