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

news2024/9/30 11:29:18

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.coloradmin.cn/o/2179502.html

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

相关文章

Hadoop FileSystem Shell 常用操作命令

提示:本文章只总结一下常用的哈,详细的命令大家可以移步官方的文档(链接贴在下面了哈🤣)— HDFS官方命令手册链接。 目录 1. cat 命令:查看 HDFS 文件内容2. put 命令:将本地文件上传到 HDFS3.…

基于VirtualBox和Ubuntu的虚拟环境搭建

VirtualBox简介 VirtualBox 是一款开源虚拟机软件。 是由德国 Innotek 公司开发,由Sun Microsystems公司出品的软件,使用Qt编写,在 Sun 被 Oracle 收购后正式更名成 Oracle VM VirtualBox。简单易用,可虚拟的系统包括Windows&…

浅谈汽车智能座舱如何实现多通道音频

一、引言 随着汽车智能座舱的功能迭代发展,传统的 4 通道、6 通道、8 通道等音响系统难以在满足驾驶场景的需求,未来对于智能座舱音频质量和通道数会越来越高。接下来本文将浅析目前智能座舱如何实现音频功放,以及如何实现多路音频功放方案。…

Library介绍(三)

环境描述 工作条件 一般lib文件里面包含了芯片的工作条件即operation conditions,其指定了工艺(process)、温度(temperature)和电压(voltage),见图1。 其中,process代表了…

10款超好用的图纸加密软件推荐!2024企业图纸加密最佳选择!

在现代企业中,保护知识产权和敏感数据显得尤为重要。尤其是工程设计、建筑图纸等专业文件,若遭到泄露,可能导致巨大的经济损失和竞争优势的丧失。为了帮助企业有效保护图纸,本文将推荐10款超好用的图纸加密软件。 1.安秉图纸加密软…

【CSS in Depth 2 精译_043】6.5 CSS 中的粘性定位技术 + 本章小结

当前内容所在位置(可进入专栏查看其他译好的章节内容) 第一章 层叠、优先级与继承(已完结)第二章 相对单位(已完结)第三章 文档流与盒模型(已完结)第四章 Flexbox 布局(已…

在起草行业标准时,如何确保格式统一?

一、遵循相关规范 1. 严格按照国家有关标准编写规范以及行业特定的标准编写指南进行起草。熟悉并掌握规范中对于标准结构、条款编号、字体字号、图表格式等方面的具体要求。 2. 参考已发布的同行业成熟标准的格式,学习其成功的格式布局和表达形式。 二、建立模板 1…

9.24-k8s服务发布

Ingress 使用域名发布 K8S 服务 部署项目 一、先部署mariadb [rootk8s-master ~]# mkdir aaa [rootk8s-master ~]# cd aaa/ [rootk8s-master aaa]# # 先部署mariadb [rootk8s-master aaa]# # configmap [rootk8s-master aaa]# vim mariadb-configmap.yaml apiVersion: v1 ki…

Leecode刷题之路第五天之最长回文子串

题目出处 05-最长回文子串 题目描述 个人解法 思路: 1.找出所有子串 2.过滤回文子串 3.回文子串长度排序 4.输出最长回文子串 代码示例:(Java) todo复杂度分析 todo 官方解法 最长回文子串 方法1:动态规划 思路&…

新能源汽车储充机器人:能源高效与智能调度

新能源汽车储充机器人:开启能源高效利用与智能调度的未来之门 随着全球能源危机的日益加剧和环境污染问题的不断恶化,新能源汽车成为了未来交通领域的重要发展方向。然而,新能源汽车的普及不仅需要解决电池技术的瓶颈,还需要构建一…

【STM32开发笔记】移植AI框架TensorFlow到STM32单片机【上篇】

【STM32开发笔记】移植AI框架TensorFlow到STM32单片机【上篇】 一、TFLM是什么?二、TFLM开源项目2.1 下载TFLM源代码2.2 TFLM基准测试说明2.3 TFLM基准测试命令 三、TFLM初步体验3.1 PC上运行Keyword基准测试3.2 PC上运行Person detection基准测试3.3 No module nam…

netty之基础aio,bio,nio

前言 在Java中,提供了一些关于使用IO的API,可以供开发者来读写外部数据和文件,我们称这些API为Java IO。IO是Java中比较重要知识点,且比较难学习的知识点。并且随着Java的发展为提供更好的数据传输性能,目前有三种IO共…

怎么在Windows系统中使用Chrome的语音搜索功能

在Windows系统中使用谷歌浏览器的语音搜索功能是一种快速且方便的搜索方式。本文将详细介绍如何在Windows系统中启用和使用Chrome的语音搜索功能。 (本文由https://chrome.cmrrs.com/站点的作者进行编写,转载时请进行标注。) 一、 启用语音搜…

《Cell》|单细胞+空间转录组绘制人类鳞状细胞癌组成和空间结构图谱

文章信息 文章题目:Multimodal Analysis of Composition and Spatial Architecture in Human Squamous Cell Carcinoma 发表期刊:《Cell》 影响因子:45.5 PART 1 研究背景 皮肤鳞状细胞癌(cSCC)主要特征是组织极…

如果您忘记了 Apple ID 和密码,按照指南可重新进入您的设备

即使您的 iPhone 或 iPad 由于各种原因被锁定或禁用,也可以使用 iTunes、“查找我的”、Apple 支持和 iCloud 解锁您的设备。但是,此过程需要您的 Apple ID 和密码来验证所有权并移除激活锁。如果您忘记了 Apple ID 和密码,请按照我们的指南重…

基于基于微信小程序的社区订餐系统

作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏:…

64.【C语言】再议结构体(下)

本文衔接第63篇63.【C语言】再议结构体(上) 目录 目录 6.复习 7.修改默认对齐数 8.结构体传参 01.传递非指针参数 02.传递指针参数(传递地址) 03.对比 9.结构体实现位段 01.位段的定义 02.格式 03.例题 答案速查 分析 10.位段跨平台问题 11.位段的应用 12.其他…

ubuntu20.04系统下,c++图形库Matplot++配置

linux下安装c图形库Matplot,使得c可以可视化编程;安装Matplot之前,需要先安装一个gnuplot,因为Matplot是依赖于此库 gnuplot下载链接: http://www.gnuplot.info/ 一、gnuplot下载与安装(可以跳过,下面源码…

EasyCVR视频汇聚平台:解锁视频监控核心功能,打造高效安全监管体系

随着科技的飞速发展,视频监控技术已成为现代社会安全、企业管理、智慧城市构建等领域不可或缺的一部分。EasyCVR视频汇聚平台作为一款高性能的视频综合管理平台,凭借其强大的视频处理、汇聚与融合能力,在构建智慧安防/视频监控系统中展现出了…

如何设计能吸引下载的截图以及注意事项

应用商店优化包括两个方面:关键字优化(吸引人们查看您的应用页面)和屏幕截图优化(将浏览量转化为下载量)。两者相互依存,两者相辅相成,让您的应用获得应有的下载量。但是,如果浏览量…