06 FreeRTOS 互斥量(mutex)

1、互斥量的使用场景

        用于保护临界资源,在多任务系统中,任务A正在使用某个资源,还没用完的情况下任务B也来使用的话,就可能导致问题。 比如对于串口,任务A正使用它来打印,在打印过程中任务B也来打印,客户看到的结果就是AB的信 息混杂在一起。

        在使用二进制信号量达到互斥的效果的时候,不能保证谁上锁谁解锁的问题。假如A在上锁然后去上厕所,这时B来解锁了,然后C来发现没有锁也来上厕所,那么这时A就曝光了。

        虽然互斥量也不能在代码上解决谁上锁谁解锁的问题,但是它可以解决优先级反转、递归上锁/解锁的问题。

        优先级反转:假设任务A、B、C的优先级分别为1、2、3,因为A先运行去获得了锁,后面C去抢占获得锁时,因为没有锁了所以被阻塞了,这时B运行,加入B运行过程中一直没有放弃CPU资源,这时候A没法执行,不能去放锁,而C此时也一直被阻塞。这就是高优先级的反而不能执行,这种现象就叫优先级反转。

        解决这种可以通过优先级继承,A开始执行,执行过程中去获得这个锁,然后轮到B执行,因为B的优先级比较高,所以B可以抢占A,然后轮到C来执行,C的优先级更高,可以抢占B,C来调用lock的时候,因为锁已经被A拿了,于是C会进入阻塞状态,在C调用lock的时候还会做个优先级继承,将自身的优先级继承给A,A的优先级变成了3,这时候A就可以执行去放锁,放锁之后又轮到C来执行。

        递归上锁:假设在任务A、B中都有上锁和解锁,在任务A执行过程中,去调用任务B,但是在B执行时,锁已经在之前被A获得并且没有释放,这就会造成递归上锁,这时任务A、B会进入阻塞状态,造成死锁。

        解决这种可以通过递归锁,即任务A在持有这个锁的过程中还可以继续去上锁。

2、互斥量函数

2.1 创建

        互斥量是一种特殊的二进制信号量。 使用互斥量时,先创建、然后去获得、释放它。使用句柄来表示一个互斥量。

        创建互斥量的函数有2种:动态分配内存,静态分配内存。

/* 创建一个互斥量,返回它的句柄。* 此函数内部会分配互斥量结构体* 返回值: 返回句柄,非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateMutex( void );
/* 创建一个互斥量,返回它的句柄。* 此函数无需动态分配内存,所以需要先有一个StaticSemaphore_t结构体,并传入它的指针* 返回值: 返回句柄,非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer);

        要想使用互斥量,需要在配置文件FreeRTOSConfig.h中定义:

#define configUSE_MUTEXES 1

 2.2 其它函数

        要注意的是,互斥量不能在ISR中使用。 各类操作函数,比如删除、give/take,跟一般是信号量是一样的。

/** xSemaphore: 信号量句柄,你要删除哪个信号量, 互斥量也是一种信号量
*/
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );/* 释放 */
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );/* 释放(ISR版本) */
BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,BaseType_t *pxHigherPriorityTaskWoken
);/* 获得 */
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,TickType_t xTicksToWait
);/* 获得(ISR版本) */
xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,BaseType_t *pxHigherPriorityTaskWoken
);

2.3 递归锁函数

        递归锁的函数跟一般互斥量的函数名不一样,参数类型一样。

递归锁
一般互斥量
创建
xSemaphoreCreateRecursiveMutex
xSemaphoreCreateMutex
获得
xSemaphoreTakeRecursive
xSemaphoreTake
释放
xSemaphoreGiveRecursive
xSemaphoreGive
/* 创建一个递归锁,返回它的句柄。* 此函数内部会分配互斥量结构体* 返回值: 返回句柄,非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void );/* 释放 */
BaseType_t xSemaphoreGiveRecursive( SemaphoreHandle_t xSemaphore );/* 获得 */
BaseType_t xSemaphoreTakeRecursive(SemaphoreHandle_t xSemaphore,TickType_t xTicksToWait
);

3、示例代码

3.1 互斥

        要配置一下#define configUSE_MUTEXES 1

static SemaphoreHandle_t xSemUART;void TaskGenericFunction( void * param)
{while(1){		xSemaphoreTake(xSemUART, portMAX_DELAY);	//在使用串口前先take这个信号量printf("%s\r\n", (char *)param);xSemaphoreGive(xSemUART);	//用完释放掉vTaskDelay(1);}	
}//main函数中
xSemUART = xSemaphoreCreateMutex();	//创建互斥量,互斥量初始值是1xTaskCreate(TaskGenericFunction, "Task3", 100, "Task 3 is running", 1, NULL);
xTaskCreate(TaskGenericFunction, "Task4", 100, "Task 4 is running", 1, NULL);

3.2 优先级反转/继承

static volatile uint8_t flagLPTaskRun = 0;
static volatile uint8_t flagMPTaskRun = 0;
static volatile uint8_t flagHPTaskRun = 0;/* 互斥量/二进制信号量句柄 */
SemaphoreHandle_t xLock;static void vLPTask( void *pvParameters )
{const TickType_t xTicksToWait = pdMS_TO_TICKS( 10UL );	uint32_t i;char c = 'A';printf("LPTask start\r\n");/* 无限循环 */for( ;; ){	flagLPTaskRun = 1;flagMPTaskRun = 0;flagHPTaskRun = 0;/* 获得互斥量/二进制信号量 */xSemaphoreTake(xLock, portMAX_DELAY);/* 耗时很久 */printf("LPTask take the Lock for long time");for (i = 0; i < 500; i++) {flagLPTaskRun = 1;flagMPTaskRun = 0;flagHPTaskRun = 0;printf("%c", c + i);}printf("\r\n");/* 释放互斥量/二进制信号量 */xSemaphoreGive(xLock);vTaskDelay(xTicksToWait);}
}static void vMPTask( void *pvParameters )
{const TickType_t xTicksToWait = pdMS_TO_TICKS( 30UL );	flagLPTaskRun = 0;flagMPTaskRun = 1;flagHPTaskRun = 0;printf("MPTask start\r\n");/* 让LPTask、HPTask先运行 */	vTaskDelay(xTicksToWait);/* 无限循环 */for( ;; ){	flagLPTaskRun = 0;flagMPTaskRun = 1;flagHPTaskRun = 0;}
}static void vHPTask( void *pvParameters )
{const TickType_t xTicksToWait = pdMS_TO_TICKS( 10UL );	flagLPTaskRun = 0;flagMPTaskRun = 0;flagHPTaskRun = 1;printf("HPTask start\r\n");/* 让LPTask先运行 */	vTaskDelay(xTicksToWait);/* 无限循环 */for( ;; ){	flagLPTaskRun = 0;flagMPTaskRun = 0;flagHPTaskRun = 1;printf("HPTask wait for Lock\r\n");/* 获得互斥量/二进制信号量 */xSemaphoreTake(xLock, portMAX_DELAY);flagLPTaskRun = 0;flagMPTaskRun = 0;flagHPTaskRun = 1;/* 释放互斥量/二进制信号量 */xSemaphoreGive(xLock);}
}int main( void )
{prvSetupHardware();/* 创建互斥量/二进制信号量 */xLock = xSemaphoreCreateBinary( );xSemaphoreGive(xLock);if( xLock != NULL ){/* 创建3个任务: LP,MP,HP(低/中/高优先级任务)*/xTaskCreate( vLPTask, "LPTask", 1000, NULL, 1, NULL );xTaskCreate( vMPTask, "MPTask", 1000, NULL, 2, NULL );xTaskCreate( vHPTask, "HPTask", 1000, NULL, 3, NULL );/* 启动调度器 */vTaskStartScheduler();}else{/* 无法创建互斥量/二进制信号量 */}/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */return 0;
}

        该程序实现了优先级反转的现象 

        只要将上述程序中的代码做出以下修改即可解决 

int main( void )
{/* 创建互斥量/二进制信号量 *///xLock = xSemaphoreCreateBinary( );//xSemaphoreGive(xLock);xLock = xSemaphoreCreateMutex();    //在这里修改}

3.3 递归锁

        互斥量的本意是:谁持有,就由谁释放。

        但是FreeRTOS并没有实现这一点,Linux也没有,A持有,B也可以释放。

        通过递归锁实现了:谁持有,就由谁释放;递归上锁/解锁。

        设置一个程序,任务四和任务五交替使用串口打印,之后让任务五来偷偷解锁。

void TaskGenericFunction( void * param)
{while(1){		xSemaphoreTake(xSemUART, portMAX_DELAY);	//在使用串口前先take这个信号量printf("%s\r\n", (char *)param);xSemaphoreGive(xSemUART);	//用完释放掉vTaskDelay(1);}	
}void Task5Function( void * param)
{vTaskDelay(10);while(1){while(1){if(xSemaphoreTake(xSemUART, 0) != pdTRUE){xSemaphoreGive(xSemUART);	//放锁}else{break;}}printf("%s\r\n", (char *)param);xSemaphoreGive(xSemUART);	//放锁vTaskDelay(1);}	
}//main函数中
xTaskCreate(TaskGenericFunction, "Task3", 100, "Task 3 is running", 1, NULL);
xTaskCreate(TaskGenericFunction, "Task4", 100, "Task 4 is running", 1, NULL);
xTaskCreate(Task5Function, "Task5", 100, "Task 5 is running", 1, NULL);

        通过结果发现后面串口打印出来的数据乱套了,证明一般的互斥量没有实现谁上锁就由谁来解锁。

        修改上述代码通过递归锁来查看。

        使用递归锁要先定义#define configUSE_RECURSIVE_MUTEXES 1

void TaskGenericFunction( void * param)
{while(1){		xSemaphoreTakeRecursive(xSemUART, portMAX_DELAY);	//在使用串口前先take这个信号量printf("%s\r\n", (char *)param);xSemaphoreGiveRecursive(xSemUART);	//用完释放掉vTaskDelay(1);}	
}void Task5Function( void * param)
{vTaskDelay(10);while(1){while(1){if(xSemaphoreTakeRecursive(xSemUART, 0) != pdTRUE){xSemaphoreGiveRecursive(xSemUART);	//放锁}else{break;}}printf("%s\r\n", (char *)param);xSemaphoreGiveRecursive(xSemUART);	//放锁vTaskDelay(1);}	
}//main函数中
xSemUART = xSemaphoreCreateRecursiveMutex();	//创建递归锁

        通过结果可以看出几个任务都正常的使用串口了,完成了谁上锁就由谁来解锁的问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/18857.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Cobaltstrike渗透测试框架

Cobaltstrike简介 cobalt strike&#xff08;简称CS&#xff09;是一款团队作战渗透测试神器&#xff0c;分为客户端及服务端&#xff0c;一个服务端可以对应多个客户 端&#xff0c;一个客户端可以连接多个服务端&#xff0c;可被团队进行分布式协团操作. 和MSF关系 metas…

使用screw-core生成数据库结构说明文档

官方项目地址&#xff1a; screw: 简洁好用的数据库表结构文档工具&#xff0c;支持MySQL/MariaDB/SqlServer/Oracle/PostgreSQL/TIDB/CacheDB 数据库。 数据库支持 MySQL MariaDB TIDB Oracle SqlServer PostgreSQL Cache DB&#xff08;2016&#xff09; H2 &#xff08;开发…

文心智能体平台 | 想象即现实

目录 文心智能体平台介绍平台简介通过平台能做什么平台的优势智能体介绍智能体类型AI 插件介绍 动手创建一个智能体访问平台并进行账号注册根据适合的方式选择智能体类型快速创建智能体智能体个性化模块配置 总结注意事项我的智能体 文心智能体平台介绍 平台简介 文心智能体平…

产品推荐 | 基于Xilinx Zynq-7015 FPGA的MYC-C7Z015开发板

一、产品概述 基于 Xilinx Zynq-7015&#xff0c;双Cortex-A9FPGA全可编程处理器&#xff1b;PS部分(ARM)与PL部分(FPGA)之间采用AXI高速片上总线通信&#xff0c;吉比特级带宽&#xff0c;突破传统ARMFPGA架构的通信瓶颈&#xff0c;通过PL部分(FPGA)灵活配置丰富的外设接口&…

若依框架官网

RuoYi 若依官方网站 |后台管理系统|权限管理系统|快速开发框架|企业管理系统|开源框架|微服务框架|前后端分离框架|开源后台系统|RuoYi|RuoYi-Vue|RuoYi-Cloud|RuoYi框架|RuoYi开源|RuoYi视频|若依视频|RuoYi开发文档|若依开发文档|Java开源框架|Java|SpringBoot|SrpingBoot2.0…

LLM - 模型下载与 git-lfs 安装

目录 一.引言 二.安装 git lfs 1.使用 apt-get 安装 2.使用 Brew 安装 3.LFS 验证 三.总结 一.引言 在 HuggingFace 上下载模型时提供一个 git clone 的指令&#xff0c;执行后可以下载对应模型的模型文件: 但是本机还没有 git lfs 命令: git: lfs is not a git comman…

Google使用AI改进了 Sheets;开源视觉语言模型llama3v;开源情绪语音模型ChatTTS;

✨ 1: Google has improved Sheets with AI. Google 使用 AI 改进了 Sheets 您可以使用 Gemini 处理您的数据并将其变成老师。 优化您的数据 Gemini 了解您的数据并提出改进建议。 例如&#xff0c;它可以将重复数据转换为更实用的下拉框。 解释数据 通过单击双子座图标…

Windows下PostgreSQL数据库的备份与恢复

文章目录 一、备份1.找到PostgreSQL的安装目录下的"bin"目录2.在windows的命令窗口里&#xff0c;使用pg_dump进行备份1.打开命令窗口2.使用pg_dump将数据库备份下来 二、恢复1.找到PostgreSQL的安装目录下的"bin"目录2.在windows的命令窗口里&#xff0c;…

全新PSAI设计插件 —— StartAI,让想象触手可及!

告别繁琐的设计过程&#xff0c;StartAI将为你的创作注入新动力&#xff0c;让每一个设计瞬间变得生动而独特。 核心功能介绍&#xff1a; 高清修复 - 每一个设计细节都至关重要&#xff0c;StartAI的高清修复可以细节优化&#xff0c;确保你的设计完美无瑕。 百变生图风格- 从…

【信息学奥赛】两个整型变量的值交换

【信息学奥赛】两个整型变量的值交换 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 编写如下一个函数&#xff0c;用于将两个整型变量的值交换 输入&#xff1a; 两个数 输出&#xff1a; 交换后的两个数 样例输入&#xff1a; 3 2样…

PyTorch自定义张量操作开发指南【CFFI+CUDA】

PyTorch 与 TensorFlow 一起成为深度学习研究人员和从业者的标准。虽然 PyTorch 在张量运算或深度学习层方面提供了多种选择&#xff0c;但一些专门的操作仍然需要手动实现。在运行时至关重要的情况下&#xff0c;应使用 C 或 CUDA 来完成此操作&#xff0c;以支持 CPU 和 GPU …

linux系统——htop命令检测

在之前提到top命令可以检测进程情况&#xff0c;但需要额外一些参数才能更清晰得到一些数据&#xff0c;htop也是进程监测命令&#xff0c;但更为准确&#xff0c;给出信息更为详实

H2RSVLM:引领遥感视觉语言模型的革命

随着人工智能技术的飞速发展&#xff0c;遥感图像理解在环境监测、气候变化、粮食安全和灾害预警等多个领域扮演着越来越重要的角色。然而&#xff0c;现有的通用视觉语言模型&#xff08;VLMs&#xff09;在处理遥感图像时仍面临挑战&#xff0c;主要因为遥感图像的独特性和当…

stream-实践应用-统计分析

背景 业务部门提供了一个数据&#xff0c;数据甚至不是excel类型的&#xff0c;是data.txt&#xff0c;每一行都是一个数据&#xff0c;需要对此数据进行统计分析 统计各个月份的销量 因为直接获取resources下的data.txt&#xff0c;所以要借助输入流进行获取数据&#xff0c;再…

【手把手带你微调 Llama3】 改变大模型的自我认知,单卡就能训

微调Llama3的自我认知后 当你问Llama3中文问题&#xff1a; “你叫什么名字&#xff1f;”、 “做个自我介绍”、 “你好” Llama3 会用中文回答 &#xff1a; “我是AI在手” &#xff08;如下图&#xff09; 1、环境安装 # nvidia 显卡 显存16G# pytorch 2.2.2 …

【Java】【python】leetcode刷题记录--双指针

双指针也一般称为快慢指针&#xff0c;主要用于处理链表和数组等线性数据结构。这种技巧主要涉及到两个指针&#xff0c;一个快指针&#xff08;通常每次移动两步&#xff09;和一个慢指针&#xff08;通常每次移动一步&#xff09;。快指针可以起到’探路‘的作用&#xff0c;…

S32K --- FLS MCAL配置

一、前言 二、MCAL配置 添加一个Mem_43_infls的模块, infls是访问内部flash, exfls是访问外部flash 2.1 General 这边暂时保持的默认,还没详细的去研究,等有空研究了,我再来更新 2.2 MemInstance 双金“index”的下标“0”可以进里面详细配置,这个是基本操作了。 2.2.1 G…

关于Word目录的更新

左侧标题顺序如有调整&#xff0c;自动目录并不会同步更新&#xff0c;每次都要记得在正文目录左上角点击更新目录

2024-05-29 服务器开发-c++线程池与task-思考

摘要: 无论是什么系统&#xff0c;线程池和task都是给上层所提供的基础的功能单元。本文记录一些核心的设计思想。 线程池要面对的场景: 调用下层接口时&#xff0c;被IO阻塞&#xff0c;导致整个服务无法对外提供服务更上层调用本模块接口时&#xff0c;是需要做到同步&#…

面向链接预测的知识图谱表示学习方法综述

源自&#xff1a;软件学报 作者&#xff1a;杜雪盈, 刘名威, 沈立炜, 彭鑫 注&#xff1a;若出现无法显示完全的情况&#xff0c;可搜索“人工智能技术与咨询”查看完整文章 摘 要 作为人工智能的重要基石, 知识图谱能够从互联网海量数据中抽取并表达先验知识, 极大程度解决…