FreeRTOS 信号量(五) ------ 递归互斥信号量

news2024/11/29 2:47:51

文章目录

  • 一、递归互斥信号量简介
  • 二、创建互斥信号量
    • 1. xSemaphoreCreateRecursiveMutex()
    • 2. xSemaphoreCreateRecursiveMutexStatic()
  • 三、递归信号量创建过程分析
  • 四、释放递归互斥信号量
  • 五、获取递归互斥信号量
  • 六、递归互斥信号量使用示例


一、递归互斥信号量简介

递归互斥信号量可以看作是一个特殊的互斥信号量,已经获取了互斥信号量的任务就不能再次获取这个互斥信号量,但是递归互斥信号量不同,已经获取了递归互斥信号量的任务可以再次获取这个递归互斥信号量,而且次数不限!一个任务使用函数 xSemaphoreTakeRecursive()成功的获取了多少次递归互斥信号量就得使用函数 xSemaphoreGiveRecursive()释放多少次!比如某个任务成功的获取了 5 次递归信号量,那么这个任务也得同样的释放 5 次递归信号量。

递归互斥信号量也有优先级继承的机制,所以当任务使用完递归互斥信号量以后一定要记得释放。同互斥信号量一样,递归互斥信号量不能用在中断服务函数中。

● 由于优先级继承的存在,就限定了递归互斥信号量只能用在任务中,不能用在中断服务函数中!
● 中断服务函数不能设置阻塞时间。

要使用递归互斥信号量的话宏 configUSE_RECURSIVE_MUTEXES 必须为 1!

二、创建互斥信号量

FreeRTOS 提供了两个互斥信号量创建函数,如下表所示:
在这里插入图片描述

1. xSemaphoreCreateRecursiveMutex()

此函数用于创建一个递归互斥信号量,所需要的内存通过动态内存管理方法分配。此函数本质是一个宏,真正完成信号量创建的是函数 xQueueCreateMutex (),此函数原型如下:

SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void )

参数:
无。

返回值:
NULL: 互斥信号量创建失败。
其他值: 创建成功的互斥信号量的句柄。

2. xSemaphoreCreateRecursiveMutexStatic()

此函数也是创建递归互斥信号量的,只不过使用此函数创建递归互斥信号量的话信号量所需 要 的 RAM 需 要 由 用 户 来 分 配 , 此 函 数 是 个 宏 , 具 体 创 建 过 程 是 通 过 函 数xQueueCreateMutexStatic ()来完成的,函数原型如下:

SemaphoreHandle_t xSemaphoreCreateRecursiveMutexStatic( StaticSemaphore_t *pxMutexBuffer )

参数:
pxMutexBuffer: 此参数指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体。

返回值:
NULL: 互斥信号量创建失败。
其他值: 创建成功的互斥信号量的句柄。

三、递归信号量创建过程分析

这里只分析动态创建互斥信号量函数 xSemaphoreCreateRecursiveMutex (),此函数是个宏,定义如下:

#define xSemaphoreCreateRecursiveMutex() 
xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX )

可以看出,真正干事的是函数 xQueueCreateMutex(),互斥信号量的创建也是用的这个函数,只是在创建递归互斥信号量的时候类型选择为queueQUEUE_TYPE_RECURSIVE_MUTEX。

四、释放递归互斥信号量

递归互斥信号量有专用的释放函数:xSemaphoreGiveRecursive(),此函数为宏,如下:

#define xSemaphoreGiveRecursive( xMutex ) xQueueGiveMutexRecursive( ( xMutex ) )

函 数 的 参 数 就 是 就 是 要 释 放 的 递 归 互 斥 信 号 量 , 真 正 的 释 放 是 由 函 数
xQueueGiveMutexRecursive()来完成的,此函数代码如下:

BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex )
{
	BaseType_t xReturn;
	Queue_t * const pxMutex = ( Queue_t * ) xMutex;
	configASSERT( pxMutex );
	//检查递归互斥信号量是不是被当前任务获取的,要释放递归互斥信号量的任务肯定是当
	//前正在运行的任务。 因为同互斥信号量一样,递归互斥信号量的获取和释放要在同一个
	//任务中完成!如果当前正在运行的任务不是递归互斥信号量的拥有者就不能释放!
	if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() ) (1)
	{
		traceGIVE_MUTEX_RECURSIVE( pxMutex );
		( pxMutex->u.uxRecursiveCallCount )--; (2)
		if( pxMutex->u.uxRecursiveCallCount == ( UBaseType_t ) 0 ) (3)
		{
			( void ) xQueueGenericSend( pxMutex, NULL, \ (4)
			queueMUTEX_GIVE_BLOCK_TIME, queueSEND_TO_BACK );
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
		xReturn = pdPASS; (5)
	}
	else
	{
		xReturn = pdFAIL; (6)
		traceGIVE_MUTEX_RECURSIVE_FAILED( pxMutex );
	}
	return xReturn;
}

(1)、哪个任务获取到的递归互斥信号量,哪个任务就释放!要释放递归互斥信号量的任务肯定是当前正在运行的任务。检查这个任务是不是递归互斥信号量的拥有者,如果不是的话就不能完成释放。

(2)、uxRecursiveCallCount 减一,uxRecursiveCallCount 用来记录递归信号量被获取的次数。由于递归互斥信号量可以被一个任务多次获取,因此在释放的时候也要多次释放,但是只有在最后一次释放的时候才会调用函数 xQueueGenericSend()完成释放过程,其他的时候只是简单的将 uxRecursiveCallCount 减一即可。

(3)、当 uxRecursiveCallCount 为 0 的时候说明是最后一次释放了。

(4)、如果是最后一次释放的话就调用函数 xQueueGenericSend()完成真正的释放过程。阻塞时间是 queueMUTEX_GIVE_BLOCK_TIME,宏queueMUTEX_GIVE_BLOCK_TIME 为 0。

(5)、递归互斥信号量释放成功,返回 pdPASS。

(6)、递归互斥信号量释放未成功,返回 pdFAIL。

由于递归互斥信号量可以被一个任务重复的获取,因此在释放的时候也要释放多次,但是只有在最后一次释放的时候才会调用函数 xQueueGenericSend()完成真正的释放。其他释放的话只是简单的将 uxRecursiveCallCount 减一。

五、获取递归互斥信号量

递归互斥信号量的获取使用函数 xSemaphoreTakeRecursive(),此函数是个宏,定义如下:

#define xSemaphoreTakeRecursive( xMutex, xBlockTime )
xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) )

函数第一个参数是要获取的递归互斥信号量句柄,第二个参数是阻塞时间。真正的获取过程是由函数 xQueueTakeMutexRecursive()来完成的,此函数如下:

BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, //要获取的信号量
TickType_t xTicksToWait )//阻塞时间
{
	BaseType_t xReturn;
	Queue_t * const pxMutex = ( Queue_t * ) xMutex;
	configASSERT( pxMutex );
	traceTAKE_MUTEX_RECURSIVE( pxMutex );
	if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() ) (1)
	{
		( pxMutex->u.uxRecursiveCallCount )++; (2)
		xReturn = pdPASS;
	}
	else
	{
		xReturn = xQueueGenericReceive( pxMutex, NULL, xTicksToWait, pdFALSE ); (3)
		if( xReturn != pdFAIL )
		{
			( pxMutex->u.uxRecursiveCallCount )++; (4)
		}
		else
		{
			raceTAKE_MUTEX_RECURSIVE_FAILED( pxMutex );
		}
	}
	return xReturn;
}

(1)、判断当前要获取递归互斥信号量的任务是不是已经是递归互斥信号量的拥有者。通过这一步就可以判断出当前任务是第一次获取递归互斥信号量还是重复获取。

(2)、如果当前任务已经是递归互斥信号量的拥有者,那就说明任务已经获取了递归互斥信号量,本次是重复获取递归互斥信号量,那么就简单的将 uxRecursiveCallCount 加一,然后返回pdPASS 表示获取成功。

(3)、如果任务是第一次获取递归互斥信号量的话就需要调用函数xQueueGenericReceive()完成真正的获取过程。

(4)、第一次获取递归互斥信号量成功以后将 uxRecursiveCallCount 加一。

六、递归互斥信号量使用示例

FreeRTOS 官方提供了一个简单的示例,大家可以参考一下,示例如下:

SemaphoreHandle_t RecursiveMutex; //递归互斥信号量句柄
//某个任务中创建一个递归互斥信号量
void vATask( void * pvParameters )
{
	//没有创建创建递归互斥信号量之前不要使用!
	RecursiveMutex = xSemaphoreCreateRecursiveMutex(); //创建递归互斥信号量
	for( ;; )
	{ 
		/************任务代码**************/
	}
}

//任务调用的使用递归互斥信号量的功能函数。
void vAFunction( void )
{
	/**********其他处理代码*****************/
	if( xMutex != NULL )
	{
	//获取递归互斥信号量,阻塞时间为 10 个节拍
		if( xSemaphoreTakeRecursive( RecursiveMutex, 10 ) == pdTRUE )
		{
			/***********其他处理过程*************/
			//这里为了演示,所以是顺序的获取递归互斥信号量,但是在实际的代码中肯定
			//不是这么顺序的获取的,真正的代码中是混合着其他程序调用的。
			xSemaphoreTakeRecursive( RecursiveMutex, ( TickType_t ) 10 );
			xSemaphoreTakeRecursive( RecursiveMutex, ( TickType_t ) 10 );
			//任务获取了三次递归互斥信号量,所以就得释放三次!
			xSemaphoreGiveRecursive( RecursiveMutex);
			xSemaphoreGiveRecursive( RecursiveMutex);
			xSemaphoreGiveRecursive( RecursiveMutex);
			//递归互斥信号量释放完成,可以被其他任务获取了
		}
		else
		{ 
			/**********递归互斥信号量获取失败***********/
		}
	}
}

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

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

相关文章

Linux学习[8]文件权限深入2 默认权限umask SUID/SGID/SBIT file指令

文章目录 前言1. 默认权限umask1.1 默认权限含义1.2 查看默认权限1.3 默认权限在文件与目录的异同 2. 特殊权限2.1 SUID2.2 SGID2.3 SBIT2.4 SUID/SGID/SBIT 权限设置2.4.1 数字法2.4.2 符号法 3. file指令 前言 在linux学习6里面,通过ls -al命令列出文件的所有详细…

2023/4/30周报

目录 摘要 论文阅读 1、题目和现存问题 2、知识空间理论、GRU和自注意力机制 3、模型构建 4、实验准备 5、实验结果 深度学习 1、GRU 2、代码实例 3、GRU和GCN结合的可能性 总结 摘要 本周在论文阅读上,阅读了一篇基于自注意力机制和双向GRU神经网络的…

【MATLAB数据处理实用案例详解(17)】——利用概念神经网络实现柴油机故障诊断

目录 一、问题描述二、利用概念神经网络实现柴油机故障诊断原理三、算法步骤3.1 定义样本3.2 样本归一化3.3 创建网络模型3.4 测试3.5 显示结果 四、运行结果五、完整代码 一、问题描述 柴油机的结构较为复杂,工作状况非常恶劣,因此发生故障的可能性较大…

linux常用命令大全

作为开发者,Linux是我们必须掌握的操作系统之一。因此,在编写代码和部署应用程序时,熟练使用Linux命令非常重要。这些常用命令不得不会,掌握这些命令,工作上会事半功倍,大大提高工作效率。 一. 文件和目录…

python使用迭代法验证角谷猜想

def fun(n):print(n)while n ! 1:n 3 * n 1 if n % 2 else n / 2print(finished)for i in range(2,1000):fun(i) 1、了解了什么是"角谷猜想" 对于任意一个自然数n,若n为偶数,则将其除以2;若n为奇数,则将其乘以3,然后再加1。如此经过有限次运算后,总可以得到自然数…

【VQ-VAE-2论文精读】Generating Diverse High-Fidelity Images with VQ-VAE-2

【VQ-VAE-2论文精读】Generating Diverse High-Fidelity Images with VQ-VAE-2 0、前言Abstract1 Introduction2 Background2.1 Vector Quantized Variational AutoEncoder 3 Method3.1 Stage 1: Learning Hierarchical Latent Codes3.2 Stage 2: Learning Priors over Latent …

【STM32】知识补充 晶振的基本原理及其应用

【STM32】知识补充 晶振的基本原理及其应用 概述晶振的基本原理晶振的性能参数晶振的分类晶振的应用晶振器在 STM32 上的应用总结 概述 晶振作为现代电子技中的重要组件, 广泛应用于各种电子设备中, 起到稳定时钟信号的作用. 本文将为您解释晶振的基本原理及其在实际应用中的用…

第 02 章 OSPF实验

2.1 OSPF 回顾 2.1.1 实验目的 在 CCNA 中,我们学习到了 OSPF 是一个链路状态路由协议,和 RIP 以及 EIGRP 的最大 不同在于对于它们对于网络的认识以及根本的算法的不同。通过对 CCNA 中 OSPF 配置实验 的回顾,从中加强我们对 OSPF 的理解。…

带你学c带你飞-P7取值范围

比特位 CPU能读懂的最小单元——比特位,bit,b 字节 内存机构的最小寻址单元——字节,Byte,B 1Byte8bit 进制 怎么算 注意:int默认是signed类型,signed类型第一位是符号位 符号位 存放signed类型的存…

对比体验 ChatGPT,聊聊文心一言的优缺点

在昨天文心一言发布后,我第一时间拿到了体验的资格,但第一次使用后却不禁有些失望。他的逻辑能力极度缺乏、创造力也差点意思。不过,今天再次高强度使用后,却又让我对这款产品的想法有了些许改变。 前言 将 2023 年称为 AI 纪元…

西门子PLC沿脉冲类指令汇总

S7-1200CPU提供了四种沿脉冲指令供用户使用,分别为:扫描操作数信号边沿指令、在信号边沿置位操作数的指令、扫描RLO的信号边沿指令以及检测信号边沿指令。 信号从0--1的时刻称为上升沿,信号从1--0的时刻称为下降沿,不管是上升沿还…

【VM服务管家】VM4.0平台SDK_2.1环境配置类

目录 2.1.1 环境配置:CSharp二次开发环境配置方法2.1.2 环境配置:Qt二次开发环境配置方法2.1.3 环境配置:MFC二次开发环境配置方法2.1.4 环境配置:VB.Net二次开发环境配置方法2.1.5 环境配置:运行出现Vm.Core.Solution…

python+django+vue消防知识宣传网站

开发语言:Python 框架:django Python版本:python3.7.7 数据库:mysql 数据库工具:Navicat 开发软件:PyCharm 层随着移动应用技术的发展,越来越多的消防单位借助于移动手机、电脑完成生活中的事…

Ubuntu目录和文件的相关操作

目录 1、目录的切换 2、查看目录及文件 3、目录的常见操作 4、文件的常见操作 1、目录的切换 打开终端窗口(”ctrlaltt“) 一般使用(”pwd“)显示当前所在的目录 比如:当前目录是在home下面的,与用户…

01_JUC概述

1. JUC是什么? 在 Java 5.0 提供了 java.util.concurrent(简称JUC)包,在此包中增加了在并发编程中很常用的工具类。此包包括了几个小的、已标准化的可扩展框架,并提供一些功能实用的类,没有这些类,一些功能会很难实现或…

玩手机打电话识别监测算法 yolov8

玩手机打电话识别监测系统通过YOLOv8网络模型技术,玩手机打电话识别监测算法对现场有人玩手机抽烟打电话时可以立即自动进行抓拍存档。YOLOv8 算法的核心特性和改动可以归结为如下:提供了一个全新的 SOTA 模型,包括 P5 640 和 P6 1280 分辨率…

【全年汇总】2023年CCF人工智能会议截稿时间汇总(持续更新)

本博文是根据2022年CCF会议推荐的人工智能领域相关会议目录撰写。 一、截稿时间总览 截稿时间的总时间轴内容将会持续更新...... 往年投稿及录用情况及链接详见图片后面的内容。 二、会议详细目录 由于一些会议的投稿时间还没公开,因此根据往年投稿时间在表格中使…

石化企业数字化防爆融合通信解决方案

项目背景 石化工业是我国国民经济和社会发展的基础性、战略性产业,其发展和壮大受到了党和国家的高度重视。随着石化企业厂区规模的不断扩大以及技术的快速发展,现有石化企业专网通信系统建设相对滞后,缺乏结合人员管理、安全生产、安全通信…

商品详情API接口如何获取淘宝数据

淘宝是中国最大最受欢迎的电商平台之一,汇集了大量的商家和买家。在淘宝上热门商品的销量经常十分巨大,因此有些开发者和网站想要获取淘宝商品数据来进行一些分析。下面是一篇关于淘宝商品详情API接口获取淘宝数据的文章。 一、淘宝商品API接口介绍 淘…

HadaFS - Burst Buffer解读

背景 近几年AI,ML,HPC大火, 针对这些场景的存储技术及方案也逐步衍生出两个分支,第一支:以Lustre,BeeGFS等为代表的分布式并行文件系统, 这些文件系统对POSIX提供了很好的支持,各种…