FreeRTOS 20:互斥量(互斥信号量)操作

互斥信号量其实就是一个拥有优先级继承的二值信号量,在同步的应用中(任务与任务或中断与任务之间的同步)二值信号量最适合。互斥信号量适合用于那些需要互斥访问的应用中。在互斥访问中互斥信号量相当于一把钥匙, 当任务想要访问共享资源的时候就必须先获得这把钥匙,当访问完共享资源以后就必须归还这把钥匙,这样其他的任务就可以拿着这把钥匙去访问资源。

互斥信号量使用和二值信号量相同的 API 操作函数,所以互斥信号量也可以设置阻塞时间,不同于二值信号量的是互斥信号量具有优先级继承的机制。当一个互斥信号量正在被一个低优先级的任务持有时, 如果此时有个高优先级的任务也尝试获取这个互斥信号量,那么这个高优先级的任务就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级,这个过程就是优先级继承。优先级继承尽可能的减少了高优先级任务处于阻塞态的时间,并且将“优先级翻转”的影响降到最低。

优先级继承并不能完全的消除优先级翻转的问题,它只是尽可能的降低优先级翻转带来的影响。实时应用应该在设计之初就要避免优先级翻转的发生。互斥信号量不能用于中断服务函数中,原因如下:

(1) 互斥信号量有任务优先级继承的机制, 但是中断不是任务,没有任务优先级, 所以互斥信号量只能用与任务中,不能用于中断服务函数。

(2) 中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态。

FreeRTOS 提供了互斥信号量的一些相关操作函数,其中常用的互斥信号量相关 API 函数,如下表所示:

创建互斥信号量

xSemaphoreCreateMutex() 动态方式创建互斥信号量

此函数用于使用动态方式创建互斥信号量,创建互斥信号量所需的内存,由 FreeRTOS 从FreeRTOS 管理的堆中进行分配。该函数实际上是一个宏定义,在 semphr.h 中有定义,具体的代码如下所示:

/*** semphr.h* * 创建一个新的互斥锁类型的信号量实例,并返回一个句柄,通过该句柄可以引用新创建的互斥锁。** 在 FreeRTOS 内部实现中,互斥锁信号量使用一块内存来存储互斥锁结构。如果使用 `xSemaphoreCreateMutex()` 创建互斥锁,则所需的内存会自动在 `xSemaphoreCreateMutex()` 函数内部动态分配。(参见 https://www.FreeRTOS.org/a00111.html)。如果使用 `xSemaphoreCreateMutexStatic()` 创建互斥锁,则应用程序编写者必须提供内存。因此,`xSemaphoreCreateMutexStatic()` 允许在不使用任何动态内存分配的情况下创建互斥锁。** 使用此函数创建的互斥锁可以通过 `xSemaphoreTake()` 和 `xSemaphoreGive()` 宏进行访问。不能使用 `xSemaphoreTakeRecursive()` 和 `xSemaphoreGiveRecursive()` 宏。** 此类型的信号量使用优先级继承机制,因此一个任务“获取”互斥锁后必须始终在不再需要该互斥锁时“释放”它。** 互斥锁类型的信号量不能在中断服务例程中使用。** 参见 `xSemaphoreCreateBinary()`,它提供了一种可以在纯同步场景中使用的替代实现(其中一个任务或中断总是“释放”信号量,另一个总是“获取”信号量),并且可以在中断服务例程中使用。** @return 如果互斥锁成功创建,则返回指向创建的信号量的句柄。如果堆内存不足无法分配互斥锁数据结构,则返回 NULL。** 示例用法:* @code{c}* SemaphoreHandle_t xSemaphore;** void vATask( void * pvParameters )* {*     // 在调用 xSemaphoreCreateMutex() 之前不能使用信号量。*     // 这是一个宏,所以直接传递变量。*     xSemaphore = xSemaphoreCreateMutex();**     if( xSemaphore != NULL )*     {*         // 互斥锁创建成功。*         // 现在可以使用互斥锁。*     }* }* @endcode* \defgroup xSemaphoreCreateMutex xSemaphoreCreateMutex* \ingroup Semaphores*/
#if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configUSE_MUTEXES == 1 ) )#define xSemaphoreCreateMutex()    xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
#endif

函 数 xSemaphoreCreateMutex() 实 际 上 是 调 用 了 函 数xQueueCreateMutex(),函数 xQueueCreateMutex()在 queue.c 文件中有定义,具体的代码如下所示


#if ( ( configUSE_MUTEXES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
/*** 创建一个新的互斥锁类型的信号量实例。** @param ucQueueType 信号量类型,通常是 `queueQUEUE_TYPE_MUTEX`。** @return 如果互斥锁成功创建,则返回指向创建的信号量的句柄。如果创建失败,则返回 NULL。*/
QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType )
{QueueHandle_t xNewQueue;const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0;// 记录进入 xQueueCreateMutex 函数的事件traceENTER_xQueueCreateMutex( ucQueueType );// 调用通用队列创建函数,创建一个长度为 1、项目大小为 0 的队列xNewQueue = xQueueGenericCreate( uxMutexLength, uxMutexSize, ucQueueType );// 初始化互斥锁prvInitialiseMutex( ( Queue_t * ) xNewQueue );// 记录返回 xQueueCreateMutex 函数的事件traceRETURN_xQueueCreateMutex( xNewQueue );// 返回创建的互斥锁句柄return xNewQueue;
}#endif /* configUSE_MUTEXES */

函数 xQueueCreateMutex,用于创建一个互斥锁。具体功能如下:

  1. 记录进入函数的事件

    • 调用 traceENTER_xQueueCreateMutex(ucQueueType) 函数,记录进入 xQueueCreateMutex 函数的事件。这通常用于调试和跟踪程序的执行流程。

  2. 创建队列

    • 定义常量 uxMutexLength 和 uxMutexSize,分别设置为1和0。这表示创建的队列长度为1,每个项目的大小为0。

    • 调用 xQueueGenericCreate(uxMutexLength, uxMutexSize, ucQueueType) 函数,创建一个长度为1、项目大小为0的队列。这个队列实际上是一个互斥锁。

  3. 初始化互斥锁

    • 调用 prvInitialiseMutex((Queue_t *) xNewQueue) 函数,初始化创建的互斥锁。prvInitialiseMutex 函数负责设置互斥锁的初始状态,确保其可以正常使用。

  4. 记录返回函数的事件

    • 调用 traceRETURN_xQueueCreateMutex(xNewQueue) 函数,记录返回 xQueueCreateMutex 函数的事件。这同样用于调试和跟踪程序的执行流程。

  5. 返回互斥锁句柄

    • 返回创建的互斥锁句柄 xNewQueue。这个句柄可以在其他地方用于操作互斥锁。

互斥信号量的就是一个队列长度为 1 的队列, 且队列项目的大小为 0, 而队列的非空闲项目数量就是互斥信号量的资源数。 函数 xQueueCreateMutex()还会调用函数 prvInitialiseMutex()对互斥信号量进行初始化,函数 prvInitialiseMutex()在 queue.c 文件中有定义,具体的代码如下所示:

#if ( configUSE_MUTEXES == 1 )static void prvInitialiseMutex( Queue_t * pxNewQueue ){if( pxNewQueue != NULL ){/** 队列创建函数会为通用队列正确设置所有队列结构成员,* 但此函数是在创建互斥锁。覆盖那些需要不同设置的成员——* 特别是优先级继承所需的信息。*/pxNewQueue->u.xSemaphore.xMutexHolder = NULL;  // 设置互斥锁持有者为 NULLpxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX; // 设置队列类型为互斥锁/** 以防这是一个递归互斥锁。*/pxNewQueue->u.xSemaphore.uxRecursiveCallCount = 0; // 设置递归调用计数为 0traceCREATE_MUTEX( pxNewQueue );  // 记录创建互斥锁的事件/** 使互斥锁处于预期的初始状态。*/( void ) xQueueGenericSend( pxNewQueue, NULL, ( TickType_t ) 0U, queueSEND_TO_BACK );  // 发送一个空项到队列}else{traceCREATE_MUTEX_FAILED();  // 记录创建互斥锁失败的事件}}#endif /* configUSE_MUTEXES */

xSemaphoreCreateMutexStatic()静态方式创建互斥信号量


/*** semphr.h* @code{c}* SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer );* @endcode** 创建一个新的互斥锁类型的信号量实例,并返回一个句柄,通过该句柄可以引用新创建的互斥锁。** 在 FreeRTOS 内部实现中,互斥锁信号量使用一块内存来存储互斥锁结构。如果使用 `xSemaphoreCreateMutex()` 创建互斥锁,* 则所需的内存会在 `xSemaphoreCreateMutex()` 函数内部自动动态分配。(参见 https://www.FreeRTOS.org/a00111.html)* 如果使用 `xSemaphoreCreateMutexStatic()` 创建互斥锁,则应用程序编写者必须提供内存。因此,`xSemaphoreCreateMutexStatic()`* 允许在不使用任何动态内存分配的情况下创建互斥锁。** 使用此函数创建的互斥锁可以通过 `xSemaphoreTake()` 和 `xSemaphoreGive()` 宏进行访问。不能使用 `xSemaphoreTakeRecursive()`* 和 `xSemaphoreGiveRecursive()` 宏。** 这种类型的互斥锁使用优先级继承机制,因此“获取”互斥锁的任务必须始终在不再需要互斥锁时“释放”互斥锁。** 互斥锁类型的信号量不能在中断服务例程中使用。** 参见 `xSemaphoreCreateBinary()`,它提供了另一种实现,可以用于纯同步(其中一个任务或中断总是“释放”信号量,另一个总是“获取”信号量),* 并且可以在中断服务例程中使用。** @param pxMutexBuffer 必须指向一个类型为 `StaticSemaphore_t` 的变量,该变量将用于保存互斥锁的数据结构,从而避免动态内存分配。** @return 如果互斥锁成功创建,则返回指向创建的互斥锁的句柄。如果 `pxMutexBuffer` 为 `NULL`,则返回 `NULL`。** 示例用法:* @code{c}* SemaphoreHandle_t xSemaphore;* StaticSemaphore_t xMutexBuffer;** void vATask( void * pvParameters )* {*     // 在使用互斥锁之前必须先创建它。`xMutexBuffer` 传递给 `xSemaphoreCreateMutexStatic()`,*     // 因此不会尝试动态内存分配。*     xSemaphore = xSemaphoreCreateMutexStatic( &xMutexBuffer );**     // 由于没有进行动态内存分配,`xSemaphore` 不可能为 `NULL`,因此不需要检查它。* }* @endcode* \defgroup xSemaphoreCreateMutexStatic xSemaphoreCreateMutexStatic* \ingroup Semaphores*/#if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configUSE_MUTEXES == 1 ) )#define xSemaphoreCreateMutexStatic( pxMutexBuffer )    xQueueCreateMutexStatic( queueQUEUE_TYPE_MUTEX, ( pxMutexBuffer ) )
#endif

函数 xSemaphoreCreateMutexStatic()实际上是调用了函数xQueueCreateMutexStatic(), 而函数 xQueueCreateMutexStatic()在 queue.c 文件中有定义,其函数内容与函数 xQueueCreateMutex()是类似的,只是将动态创建队列的函数替换成了静态创建队列的函数。

#if ( ( configUSE_MUTEXES == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) )QueueHandle_t xQueueCreateMutexStatic( const uint8_t ucQueueType,StaticQueue_t * pxStaticQueue ){QueueHandle_t xNewQueue;const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0;traceENTER_xQueueCreateMutexStatic( ucQueueType, pxStaticQueue );  // 记录进入 xQueueCreateMutexStatic 函数的事件/* 防止编译器在 configUSE_TRACE_FACILITY 不等于 1 时发出未使用参数的警告。 */( void ) ucQueueType;/* 调用 xQueueGenericCreateStatic 函数创建一个静态互斥锁队列。 */xNewQueue = xQueueGenericCreateStatic( uxMutexLength, uxMutexSize, NULL, pxStaticQueue, ucQueueType );/* 初始化新创建的互斥锁队列。 */prvInitialiseMutex( ( Queue_t * ) xNewQueue );traceRETURN_xQueueCreateMutexStatic( xNewQueue );  // 记录从 xQueueCreateMutexStatic 函数返回的事件return xNewQueue;  // 返回新创建的互斥锁队列的句柄}#endif /* configUSE_MUTEXES */

  1. 记录进入事件

    • traceENTER_xQueueCreateMutexStatic( ucQueueType, pxStaticQueue );:记录进入 xQueueCreateMutexStatic 函数的事件。

  2. 防止编译器警告

    • ( void ) ucQueueType;:防止编译器在 configUSE_TRACE_FACILITY 不等于1时发出未使用参数的警告。

  3. 创建静态队列

    • xNewQueue = xQueueGenericCreateStatic( uxMutexLength, uxMutexSize, NULL, pxStaticQueue, ucQueueType );:调用 xQueueGenericCreateStatic 创建一个长度为1、大小为0的静态队列。

  4. 初始化互斥锁队列

    • prvInitialiseMutex( ( Queue_t * ) xNewQueue );:初始化新创建的互斥锁队列。

  5. 记录返回事件

    • traceRETURN_xQueueCreateMutexStatic( xNewQueue );:记录从 xQueueCreateMutexStatic 函数返回的事件。

  6. 返回互斥锁队列句柄

    • return xNewQueue;:返回新创建的互斥锁队列的句柄。

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

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

相关文章

MongoDB笔记03-MongoDB索引

文章目录 一、前言1.1 概述1.2 MongoDB索引使用B-Tree还是BTree?1.3 B 树和 B 树的对比1.4 总结 二、索引的类型2.1 单字段索引2.2 复合索引2.3 其他索引 三、索引的管理操作3.1 索引的查看3.2 索引的创建3.2.1 单字段索引3.2.2 复合索引 3.3 索引的移除3.3.1 指定索…

肿瘤治疗评价指标太多?一文帮你梳理清楚!|个人观点·24-11-09

小罗碎碎念 如何延长癌症患者存活时间、提高生存质量、减轻肿瘤带来的痛苦,是评价抗癌药物的重要标准,而把这些标准落在数据上就诞生了各项“评价指标”。 在肿瘤治疗领域,有OS、PFS、RFS、TTP、TTF、ORR、DCR、DDC等各项评价指标。对于大部…

保研考研机试攻略:python笔记(3)

🐨🐨🐨11sort 与 sorted 区别 sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作。 list 的 sort 方法返回的是对已经存在的列表进行操作, 无返回值,而内建函数 sorted 方法返回的…

Linux之自定义shell和C标准库函数

自定义shell和C标准库函数 一.自定义xshell1.1main函数主体1.2获取用户信息以及命令串1.3判断命令串是否为空串1.4判断是否为重定向1.5分割命令串1.6判断是否为内建命令1.7执行命令 二.自定义C标准库函数2.1mystdio.h2.2mystdio.c2.3main.c 一.自定义xshell 1.1main函数主体 1…

TeamTalk知识点梳理一(单聊)

文章目录 db_proxy_serverdb_proxy_server reactor响应处理流程连接池redis连接池MySQL连接池 单聊消息消息如何封装?如何保证对端完整解析一帧消息?协议格式?单聊消息流转流程消息序号(msg_id )为什么使用redis生成&a…

带跳转功能的电子产品目录如何制作?

​在数字化时代,电子产品已成为我们生活和工作中不可或缺的伙伴。为了方便用户快速查找所需产品,带跳转功能的电子产品目录应运而生。本文将详细介绍如何制作一个高效便捷的带跳转功能电子产品目录,让用户轻松实现产品查找、筛选和购买。 1.要…

从0开始linux(26)——动静态库

欢迎来到博主的专栏:从0开始linux 博主ID:代码小豪 文章目录 如何写一个静态库动态库动静态链接 c/c程序形成可执行程序,需要经过三个步骤,编译、汇编、链接三个步骤,我们之前做链接时,使用的方法是将头文件…

hexo 搭建个人博客网站

hexo搭建个人博客 文章目录 hexo搭建个人博客摘要搭建网站的前置工具WebStormHexo Hexo配置初始化本地运行 主题更改安装butterfly主题 正式上线GitHub Pages配置新建仓库SSH密钥配置 将hexo部署到GitHub 个性化设置后续网站更新内容分类和标签设置分类(categories&…

BLDC基础知识复习【二】

如果采用20毫欧的电流采样电阻,10A的电流计算出来时0.2V,这个显然还是太小了,需要运放放大并且加上偏置: 6组换向程序: 最核心的控制逻辑在这里:在main.c里面对PWM占空比进行设置,通过一个指针在…

1130 - Host ‘10.0.0.1‘ is not allowed to connect to this MySQL server

1130 - Host 10.0.0.1 is not allowed to connect to this MySQL server 一、1130 - Host 10.0.0.1 is not allowed to connect to this MySQL server二、1130 - Host 10.0.0.1 is not allowed to connect to this MariaDB serverendl 一、1130 - Host ‘10.0.0.1’ is not all…

构建智慧城市:数字孪生技术的发展之路

基于数字孪生的智慧城市发展是一种革命性的城市转型模式,旨在将物理世界与数字世界融合,在数字平台上建立城市的虚拟映像,从而实现对城市运行状态、资源利用、环境影响等方面的综合管理和优化。这种发展模式将数字技术深度融入城市规划、建设…

金融行业信息流投放方法论及金融客户投放案例

失血2024,金融行业进入“极寒”,广告投放也不例外。 受金融政策管控,在渠道投放受限也颇多,创意文案及素材上审核异常严格,整体投放成本高…… 金融理财信息流广告投放,如带着“镣铐”跳舞,束…

Unity-Yaml-Dot-Net诗歌篇-如何像雷总学习写代码像诗歌-MVC 框架,+注入Inject +状态机生命周期

我们是否可以像雷总一样 大家都说他的代码,像诗一样优雅 一个MVC 框架,加注入 (以下内容其实和雷总没什么关系,也和雷总当年代码毫无关系,不过先“阅读理解”一下) 雷总-写的代码像似一个优雅??!!^^ R…

安卓好软-----电脑端查看apk全部信息的工具 查看包名 名称以及权限等等

有时候从网络下载的应用很多是英文。时间久了会忘记到底是什么apk应用。这款工具可以方便的查看apk应用的名称 包名以及各种权限 图标 大小版本号等等。方便用户随时查看 APK Helper能够详细地获得安装包名、软件名称、APK证书、真实版本号、要求的手机版本、系统权限、以及证书…

分布式唯一ID生成(四):tinyid

文章目录 本系列前言号段模式多DB支持tinyid-client 本系列 漫谈分布式唯一ID分布式唯一ID生成(二):leaf分布式唯一ID生成(三):uid-generator分布式唯一ID生成(四):tiny…

JavaWeb:文件上传2

欢迎来到“雪碧聊技术”CSDN博客! 在这里,您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者,还是具有一定经验的开发者,相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导,我将…

April tag坐标定义

朝右的方向为x轴正向, 朝下的方向为y轴正方向, z 轴垂直纸面向里。 4个角点的坐标定义如下图所示。

江行智能×图扑软件:输煤皮带数字孪生管控系统

在现代矿业和电力行业中,输煤皮带系统是运输环节的核心。然后,随着智能化生产的高要求,智慧矿山输煤皮带系统的传统管理模式已显得力不从心。产业引入的三维可视化和数字孪生技术,一改传统输煤皮带系统由于设备老化、管理不善等原…

【Python】从入门开始抓取你想要的电影,一周可掌握基础,附完整源码

Python学习很简单,只是你走进了误区。 为什么你一定要先掌握枯燥的基础点后,再去做实际操作呢? 其实,你根本坚持不了那么长时间,但实际上你可以直接去做python项目。 不信?看看我做这个项目的思路&#x…

【C++】map和set的介绍及使用

前言: map和 set 是 C STL(标准模板库)中的两种非常重要的容器,它们基于一种叫做平衡二叉搜索树(通常是红黑树)的数据结构来实现。在 C 中,map 是一个键值对容器,set 只存储唯一的键…