STM32F1+HAL库+FreeTOTS学习15——互斥信号量

STM32F1+HAL库+FreeTOTS学习15——互斥信号量

  • 1. 优先级翻转
  • 2. 互斥信号量
  • 3. 相关API函数;
    • 3.1 互斥信号量创建
    • 3.2 获取信号量
    • 3.3 释放信号量
    • 3.4 删除信号量
  • 4. 操作实验
    • 1. 实验内容
    • 2. 代码实现
    • 3. 运行结果

上期我们介绍了数值信号量。这一期我们来介绍互斥信号量

1. 优先级翻转

在接受互斥信号量之前,我们还需要先了解一下优先级翻转:我们在学习二值信号量的时候有提到过,二值信号量的使用有可能带来任务优先级翻转的问题,所谓优先级翻转:就是优先级高的任务反而慢执行,低优先级的任务先执行,这种情况在操作系统中,我们是不希望出现的,因为会导致任务的执行顺序不按预期结果执行,可能会导致未知的结果。

在这里插入图片描述
【注】:任务优先级:任务H > 任务M > 任务L

如图就是一个典型的例子:由于任务L获取了信号量,导致任务H被阻塞,进而使得优先级高的H进入阻塞,任务M一直在执行。

2. 互斥信号量

为了解决二值信号量带来的任务优先级翻转问题,我们引入互斥信号量。

互斥信号量其实就是一个拥有优先级继承的二值信号量,在同步应用中(任务与任务或者是中断与任务的同步)二值信号量最为合适;互斥信号量则使用在需要互斥访问的常用。在互斥访问中互斥信号量就相当于一把钥匙,访问前必须获得钥匙,访问之后必须归还钥匙。

互斥信号量使用和二值信号量相同的API函数,同样可以设置阻塞时间,不同的在于互斥信号量有优先级继承机制,所谓优先级继承机制,就是当一个互斥信号量被低优先级的任务持有时,此时如果有一个高优先级的任务也要获取这个任务优先级,这个高优先级的任务会被阻塞,但是高优先级的任务会把低优先级任务的优先级提升至与自己相同,这个过程叫做优先级继承。

                                           【注】:低优先级的任务优先级只会短暂的提高,等到高优先级的任务运行时,恢复原来的优先级。

优先级继承可以有限的减少高优先级任务的阻塞时间,将优先级翻转的影响降到最低。但是无法完全消除优先级翻转问题。原因如下:

  1. 互斥信号量有优先级继承的机制,但是中断不是任务,没有优先级。所以互斥信号量在中断中并不适用
  2. 中断需要快进跨出,不允许进入阻塞。

3. 相关API函数;

互斥信号量的使用过程:创建互斥信号量->释放信号量-> 获取信号量 -> 删除信号量 ( 可选 ),下面我们围绕几个步骤介绍计数信号量的相关API函数
常用的二值信号量API函数如下表:

函数描述
xSemaphoreCreateMutex()使用动态方式创建互斥信号量
xSemaphoreCreateMutexStatic()使用静态方式创建互斥信号量
xSemaphoreTake()获取信号量
xSemaphoreGive()释放信号量
vSemaphoreDelete()删除信号量

【注】:二值、计数、互斥信号量的获取和释放函数都是相同的,不过互斥信号量没有在中断使用的函数,二值和计数信号量有。

3.1 互斥信号量创建

  1. xSemaphoreCreateMutex()

此函数用于动态方式创建互斥信号量,创建所需要的内存,有FreeRTOS自动分配,该函数实际上是一个宏定义,在semphr.h文件中有定义,具体的代码如下:

#define xSemaphoreCreateMutex() xQueueCreateMutex(queueQUEUE_TYPE_MUTEX)

可以看到xSemaphoreCreateMutex() 内部是调用了xQueueCreateMutex() ,由于该函数不经常使用,我们这里不赘述。所以我们直接把xSemaphoreCreateMutex() 当作一个函数介绍(实际上是一个宏,我们当作函数来使用,FreeRTOS的官方文档也是这样的),下面是函数原型:

/*** @brief       xSemaphoreCreateMutex* @param       无* @retval      返回值为NULL,表示创建失败,其他值表示为创建互斥信号量的句柄*/
SemaphoreHandle_t xSemaphoreCreateMutex( void );
  1. xSemaphoreCreateMutexStatic()

此函数用于静态方式创建互斥信号量,创建互斥信号量所需要的内存,需要用户手动分配,该函数实际上是一个宏定义,在semphr.h文件中有定义,具体的代码如下:

#define xSemaphoreCreateMutexStatic( pxMutexBuffer) \xQueueCreateMutexStatic( queueQUEUE_TYPE_MUTEX, \( pxMutexBuffer ) )

可以看到xSemaphoreCreateMutexStatic() 内部是调用了xQueueCreateMutexStatic() ,有用该函数不经常使用,我们这里不赘述。所以我们直接把xSemaphoreCreateMutexStatic() 当作一个函数介绍(实际上是一个宏,我们当作函数来使用,FreeRTOS的官方文档也是这样的),下面是函数原型:

/*** @brief       xSemaphoreCreateMutexStatic* @param       pxMutexBuffer :指向StaticSemaphore_t 类型的指针,用于保存互斥信号量的状态值* @retval      返回值为NULL,表示创建失败,其他值表示为创建数值信号量的句柄*/
SemaphoreHandle_t xSemaphoreCreateMutexStatic(StaticSemaphore_t *pxMutexBuffer );

3.2 获取信号量

  1. xSemaphoreTake()

此函数用于获取信号量,如果信号量处于没有资源的状态,那么可以选择将任务进入阻塞状态,如果成功获取到了信号量,那么信号的资源数减1,该函数实际上是一个宏定义,在semphr.h文件中有定义,具体的代码如下:

#define xSemaphoreTake( xSemaphore, \xBlockTime) \xQueueSemaphoreTake( ( xSemaphore ), \( xBlockTime ))

可以看到xSemaphoreTake() 内部是调用了xQueueSemaphoreTake() ,关于xQueueSemaphoreTake函数的定义和使用,我们这里不赘述。所以我们直接把xSemaphoreTake() 当作一个函数介绍(实际上是一个宏,我们当作函数来使用,FreeRTOS的官方文档也是这样的),下面是函数原型:

/*** @brief       xSemaphoreTake* @param       xSemaphore:需要获取信号量的句柄* @param       xTicksToWait:阻塞时间* @retval      返回值为pdTRUE,表示获取成功,如果返回值为pdFALSE,表示获取失败。*/BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore,TickType_t xTicksToWait );

3.3 释放信号量

  1. xSemaphoreGive()

此函数用于释放信号量,如果信号量处于资源满的状态,那么可以选择将任务进入阻塞状态,如果成功释放了信号量,那么信号的资源数加1,该函数实际上是一个宏定义,在semphr.h文件中有定义,具体的代码如下:

#define xSemaphoreGive( xSemaphore) \xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), \NULL, \semGIVE_BLOCK_TIME, \queueSEND_TO_BACK)

可以看到xSemaphoreGive() 内部是调用了xQueueGenericSend() ,该函数在 STM32F1+HAL库+FreeTOTS学习12——队列 中有介绍,我们这里不赘述。所以我们直接把xSemaphoreGive() 当作一个函数介绍(实际上是一个宏,我们当作函数来使用,FreeRTOS的官方文档也是这样的),下面是函数原型:

/*** @brief       xSemaphoreGive* @param       xSemaphore:需要释放信号量的句柄* @retval      返回值为pdTRUE,表示释放成功,如果返回值为pdFALSE,表示释放失败。*/BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );

3.4 删除信号量

  1. vSemaphoreDelete()
    此函数用于删除已创建的信号量。该函数实际上是一个宏定义,在 semphr.h 文件中有定义,具体的代码如下所示
#define vSemaphoreDelete(xSemaphore) \vQueueDelete ( QueueHandle_t ) \( xSemaphore ))

可以看到vSemaphoreDelete() 内部是调用了vQueueDelete () ,关于vQueueDelete 函数的定义和使用,我们这里不赘述。所以我们直接把vSemaphoreDelete() 当作一个函数介绍(实际上是一个宏,我们当作函数来使用,FreeRTOS的官方文档也是这样的),下面是函数原型:

/*** @brief       vSemaphoreDelete* @param       xSemaphore :需要删除信号量的句柄* @retval      无*/
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );

4. 操作实验

1. 实验内容

在STM32F103RCT6上运行FreeRTOS,通过按键控制,完成对应的数值信号量操作,具体要求如下:

  • 定义一个互斥信号量,任务1、2、3,优先级分别为1、2、3(任务1优先级最低,任务3优先级最高)
  • 任务3:获取互斥信号量,打印相关信息,完成之后释放互斥信号量
  • 任务2:打印“中断优先级任务正在运行”。
  • 任务1:和任务3操作一样,只不过延时一段时间,让优先级低的任务占用信号量久一点。

2. 代码实现

#include "freertos_demo.h"
#include "main.h"
#include "queue.h" 		//需要包含队列和任务相关的头文件
#include "task.h"
#include "key.h"		//包含按键相关头文件/*FreeRTOS*********************************************************************************************//******************************************************************************************************/
/*FreeRTOS配置*//* TASK1 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 */
#define TASK1_PRIO      1                  /* 任务优先级 */
#define TASK1_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            Task1Task_Handler;  /* 任务句柄 */
void task1(void *pvParameters);					/*任务函数*//* TASK2 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 */
#define TASK2_PRIO      2                  /* 任务优先级 */
#define TASK2_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            Task2Task_Handler;  /* 任务句柄 */
void task2(void *pvParameters);					/*任务函数*//* TASK3 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 */
#define TASK3_PRIO      3                  /* 任务优先级 */
#define TASK3_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            Task3Task_Handler;  /* 任务句柄 */
void task3(void *pvParameters);					/*任务函数*/SemaphoreHandle_t  SemaphoreMutex;				/* 定义互斥信号量 *//******************************************************************************************************//*** @brief       FreeRTOS例程入口函数* @param       无* @retval      无*/
void freertos_demo(void)
{taskENTER_CRITICAL();           /* 进入临界区,关闭中断,此时停止任务调度*//* 用于优先级翻转实验 */
//	SemaphoreMutex = xSemaphoreCreateBinary();			/* 创建二值信号量 */
//	xSemaphoreGive(SemaphoreMutex);		/* 释放互斥信号量 *//* 用于互斥信号量实验 */SemaphoreMutex = xSemaphoreCreateMutex();			/* 创建互斥信号量 */if(SemaphoreMutex != NULL){printf("互斥信号量创建成功!!!\r\n");}else{printf("互斥信号量创建失败!!!\r\n");}/* 创建任务1 */xTaskCreate((TaskFunction_t )task1,(const char*    )"task1",(uint16_t       )TASK1_STK_SIZE,(void*          )NULL,(UBaseType_t    )TASK1_PRIO,(TaskHandle_t*  )&Task1Task_Handler);/* 创建任务2 */xTaskCreate((TaskFunction_t )task2,(const char*    )"task2",(uint16_t       )TASK2_STK_SIZE,(void*          )NULL,(UBaseType_t    )TASK2_PRIO,(TaskHandle_t*  )&Task2Task_Handler);/* 创建任务3 */xTaskCreate((TaskFunction_t )task3,(const char*    )"task3",(uint16_t       )TASK3_STK_SIZE,(void*          )NULL,(UBaseType_t    )TASK3_PRIO,(TaskHandle_t*  )&Task3Task_Handler);taskEXIT_CRITICAL();            /* 退出临界区,重新开启中断,开启任务调度 */vTaskStartScheduler();		//开启任务调度
}/*** @brief       task1* @param       pvParameters : 传入参数(未用到)* @retval      无*/
void task1(void *pvParameters)
{BaseType_t errMessage;		/* 错误信息 */while(1){errMessage = xSemaphoreTake(SemaphoreMutex,portMAX_DELAY);		/* 获取互斥信号量 */if(errMessage == pdTRUE)	{printf("低优先级获取信号量成功\r\n");}else{printf("低优先级获取获取信号量失败\r\n");}HAL_Delay(3000);errMessage = xSemaphoreGive(SemaphoreMutex);		/* 释放互斥信号量 */if(errMessage == pdTRUE)	{printf("低优先级释放信号量成功\r\n");}else{printf("低优先级释放信号量失败\r\n");}vTaskDelay(1000);}
}	
/*** @brief       task2* @param       pvParameters : 传入参数(未用到)* @retval      无*/
void task2(void *pvParameters)	
{	while(1){	printf("中等优先级任务执行\r\n");vTaskDelay(1000);}
}
/*** @brief       task3* @param       pvParameters : 传入参数(未用到)* @retval      无*/
void task3(void *pvParameters)	
{	BaseType_t errMessage;		/* 错误信息 */while(1){	errMessage = xSemaphoreTake(SemaphoreMutex,portMAX_DELAY);		/* 获取互斥信号量 */if(errMessage == pdTRUE)	{printf("高优先级获取信号量成功\r\n");}else{printf("高优先级获取信号量失败\r\n");}errMessage = xSemaphoreGive(SemaphoreMutex);		/* 释放互斥信号量 */if(errMessage == pdTRUE)	{printf("高优先级释放信号量成功\r\n");}else{printf("高优先级释放信号量失败\r\n");}vTaskDelay(1000);}
}

3. 运行结果

  1. 使用互斥信号量的结果(没有优先级翻转)
    在这里插入图片描述

显然这里不是很好懂,所以我们来对比一下有优先级翻转的情况。在freertos_demo() 函数里面把信号量部分创建的代码修改一下就可以

/* 用于优先级翻转实验 */
//	SemaphoreMutex = xSemaphoreCreateBinary();			/* 创建二值信号量 */
//	xSemaphoreGive(SemaphoreMutex);		/* 释放互斥信号量 *//* 用于互斥信号量实验 */SemaphoreMutex = xSemaphoreCreateMutex();			/* 创建互斥信号量 *//*||||||↓ 									*//* 用于优先级翻转实验 */SemaphoreMutex = xSemaphoreCreateBinary();			/* 创建二值信号量 */xSemaphoreGive(SemaphoreMutex);		/* 释放互斥信号量 *//* 用于互斥信号量实验 */
//	SemaphoreMutex = xSemaphoreCreateMutex();			/* 创建互斥信号量 */
  1. 使用二值信号量的结果(有优先级翻转)
    在这里插入图片描述
    对比之下就可以看出问题了,由于优先据翻转的存在,导致任务2很多时候都是比认为3先执行(因为任务3被阻塞了,导致优先级翻转),这个情况时我们不希望的,而互斥信号量的引入,一定程度上解决了或者问题。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【计算机毕业设计】springboot企业客户信息反馈平台

摘 要 网络的广泛应用给生活带来了十分的便利。所以把企业客户信息反馈管理与现在网络相结合,利用java技术建设企业客户信息反馈平台,实现企业客户信息反馈的信息化。则对于进一步提高企业客户信息反馈管理发展,丰富企业客户信息反馈管理经验…

官网:视觉是第一记忆,没有记忆点的官网设计是失败的。

官方网站虽然不像之前那么火爆了,但是依然是企业展示品牌形象和吸引用户的重要渠道。仅仅拥有一个官方网站并不足以吸引用户,更重要的是网站的设计是否能够给用户留下深刻的记忆。 当前,用户对于网站的要求也越来越高,他们不仅仅希…

Arduino UNO R3自学笔记16 之 Arduino的定时器介绍及应用

注意:学习和写作过程中,部分资料搜集于互联网,如有侵权请联系删除。 前言:学习定时器的功能。 1.定时器介绍 定时器也是一种中断,属于软件中断。 它就像一个时钟,可以测量事件的时间间隔。 比如早…

重置linux后vscode无法再次使用ssh连接

如果你使用过vscode ssh远程连接了一个Linux系统,但该系统被重置了,并且关键配置没有改变。再次使用vscode连接时,vscode可能无法连接。 原因:vscode远程连接后会在C:\Users{{你的用户名}}.ssh下的known_hosts和known_hosts.old。…

停止模式下USART为什么可以唤醒MCU?

在MCU的停止模式下,USART之类的外设时钟是关闭的,但是USART章节有描述到在停止模式下可以用USART来对MCU进行唤醒: 大家是否会好奇在外设的时钟被关闭的情况下,USART怎么能通过接收中断或者唤醒事件对MCU进行唤醒的呢&#xff1…

2024多模态大模型发展调研

随着生成式大语言模型应用的日益广泛,其输入输出模态受限的问题日益凸显,成为制约技术进一步发展的瓶颈。为突破这一局限,本文聚焦于研究多模态信息的协同交互策略,旨在探索一种能够统一理解与生成的多模态模型构建方法。在此基础…

基于springboot+小程序的在线选课管理系统1(源码+sql脚本+视频导入教程+文档)

👉文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 基于springboot小程序的在线选课管理系统实现了管理员、教师及学生。 1、管理员实现了首页、个人中心、管理员管理、教师管理、学生管理、课程信息管理、选课信息、公告管理、论坛管理、基…

Redis哨兵模式的搭建以及配置参数简介

原理 Redis哨兵模式是一种用于在Redis主从复制环境中进行高可用性监控和故障恢复的机制。该模式引入了一个或多个哨兵节点,这些节点负责监控Redis服务器的状态,并在主节点发生故障时切换为新的主节点。 哨兵节点的工作原理如下: 1、哨兵节点…

PDF阅读器工具集萃:满足你的多样需求

现在阅读书籍大部分都喜欢电子书的形式了吧,因为小小的一个设备就能存下上万本书。从流传程度来说PDF无疑是一个使用最广的格式。除了福昕PDF阅读器阅读之外还有哪些好用的阅读工具呢/?今天我们一起来探讨一下吧。 1.福昕阅读器 链接一下>>www.f…

MongoDB微服务部署

一、安装MongoDB 1.在linux中拉去MongoDB镜像文件 docker pull mongo:4.4.18 2. 2.创建数据挂载目录 linux命令创建 命令创建目录: mkdir -p /usr/local/docker/mongodb/data 可以在sshclient工具查看是否创建成功。 进入moogodb目录,给data赋予权限777 cd …

【算法】链表:21.合并两个有序链表(easy)

系列专栏 《分治》 《模拟》 《Linux》 目录 1、题目链接 2、题目介绍 3、解法(双指针) 4、代码 1、题目链接 21. 合并两个有序链表 - 力扣(LeetCode) 2、题目介绍 3、解法(双指针) 推荐一篇题解…

计算机毕业设计Python+Spark知识图谱高考分数线预测 高考志愿推荐系统 高考数据分析 高考可视化 高考大数据 大数据毕业设计

《PythonSpark知识图谱高考分数线预测与志愿推荐系统》开题报告 一、课题背景及意义 1. 背景 随着我国高考制度的不断完善以及大数据技术的快速发展,高考志愿推荐系统的需求日益增长。高考作为中国教育体系中的重要环节,其志愿填报直接关系到考生的未…

双指针--收尾的两道题

双指针 (封面起到吸引读者作用,和文章内容无关哈,但是文章也是用心写的) 三数之和 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k ,同时还满足 nums[i] nums…

Arduino UNO R3自学笔记13 之 Arduino使用LM35如何测量温度?

注意:学习和写作过程中,部分资料搜集于互联网,如有侵权请联系删除。 前言:学习使用传感器测温。 1.LM35介绍 一般来讲当知道需求,就可以 通过既定要求的条件来筛选需要的器件,多方面的因素最终选定了器件…

鸿蒙开发需要学什么语言

随着物联网(IoT)技术的发展,操作系统作为连接人与智能设备的关键桥梁变得尤为重要。鸿蒙系统(HarmonyOS),作为华为推出的一款面向全场景的分布式操作系统,不仅在国内引起了广泛关注,在国际上也逐渐崭露头角。对于开发者而言&#…

全新升级的GUI: Depthai Viewer 使用指南发布

DepthAIViewer是一个 GUI 应用程序,可让您通过实时输出可视化图像来使用相机。 DepthAIViewer 是 DepthAI 和 OAK 相机的可视化工具。它在默认情况下将运行一个演示应用程序,该应用程序将可视化所有steam在设备上运行推理。它还允许您更改设备的配置。当…

CTMO时代下的营销新力量:2+1链动模式AI智能名片商城小程序

在当今这个瞬息万变的商业世界里,营销领域正经历着一场深刻的变革。传统的CMO岗位似乎在时代的浪潮中逐渐失去了它的光芒,CTMO正在悄然取代传统CMO的岗位。 随着营销丛林现象的出现,企业面临着前所未有的挑战。许多企业发现,那些传…

自动驾驶系列—深度剖析自动驾驶芯片SoC架构:选型指南与应用实战

🌟🌟 欢迎来到我的技术小筑,一个专为技术探索者打造的交流空间。在这里,我们不仅分享代码的智慧,还探讨技术的深度与广度。无论您是资深开发者还是技术新手,这里都有一片属于您的天空。让我们在知识的海洋中…

Vue+NestJS项目实操(图书管理后台)

一、项目搭建 前端基于vben进行二次开发 在Github下载vben框架,搜索vben即可 下载地址:https://github.com/vbenjs/vue-vben-admin 下载完成后,进行安装依赖,使用命令: // 下载依赖 pnpm install// 运行项目 pnpm …

数据分析-30-电影死亡笔记中的数据分析思维

文章目录 1 死亡笔记简介2 推理过程中的数据分析2.1 第一个问题2.2 第二个问题2.3 第三个问题3 数据分析的发展4 参考附录1 死亡笔记简介 《死亡笔记》改编自小畑健同名日本人气漫画《Death note》,故事描述拥有一本写上姓名就能将人置于死地笔记本的高中生夜神月与天才警部搜…