【freeRTOS】操作系统之三-信号量

news2024/12/23 13:27:29
1 二值信号量
1.1 二值信号量简介

​ 二值信号量通常用于互斥访问或同步,二值信号量和互斥信号量非常类似,但是还是有一 些细微的差别,互斥信号量拥有优先级继承机制,二值信号量没有优先级继承。因此二值信号量更适合用于同步(任务与任务或任务与中断的同步),而互斥信号量适合用于简单的互斥访问。

​ 和队列一样,信号量 API 函数允许设置一个阻塞时间,阻塞时间是当任务获取信号量的时 候由于信号量无效从而导致任务进入阻塞态的最大时钟节拍数。如果多个任务同时阻塞在同 一个信号量上的话那么优先级最高的哪个任务优先获得信号量,这样当信号量有效的时候高优先级的任务就会解除阻塞状态。

二值信号量其实就是一个只有一个队列项的队列,这个特殊的队列要么是满的,要么是空的,这不正好就是二值的吗? 任务和中断使用这个特殊队列不用在乎队列中存的是什么消息,只需要知道这个队列是满的还是空的。可以利用这个机制来完成任务与中断之间的同步。

​ 使用二值信号量来完成中断与任务同步的这个机制中,任务优先级确保了外设能够得到及时的处理,这样做相当于推迟了中断处理过程。也可以使用队列来替代二值信号量,在外设事件的中断服务函数中获取相关数据,并将相关的数据通过队列发送给任务。如果队列无效的话任务就进入阻塞态,直至队列中有数据,任务接收到数据以后就开始相关的处理过程。

1.2 创建二值信号量

同队列一样,要想使用二值信号量就必须先创建二值信号量,二值信号量创建函数 如表 14.2.2 所示

img

#define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE )

1.4 释放信号量

​ 不管是二值信号量、计数型信号量还是互斥信号量,释放信号量有两个函数,释放信号量的函数有两个,如表 14.2.4.1 所示。

img

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

​ 可以看出任务级释放信号量就是向队列发送消息的过程,只是这里并没有发送具体的消息, 阻塞时间为 0(宏 semGIVE_BLOCK_TIME 为 0),入队方式采用的后向入队。入队的时候队列结构体成员变量uxMessagesWaiting 会加一,对于二值信号量通过判断 uxMessagesWaiting 就可以知道信号量是否有效了,当 uxMessagesWaiting 为 1 的话说明二值信号量有效,为 0 就无效。如果队列满的话就返回错误值 errQUEUE_FULL,提示队列满,入队失败。

#define xSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken ) xQueueGiveFromISR( ( QueueHandle_t ) ( xSemaphore ), ( pxHigherPriorityTaskWoken ) )

​ 在中断中释放信号量真正使用的是函数 xQueueGiveFromISR(),此函数和中断级通用入队 函数 xQueueGenericSendFromISR()极其类似!只是针对信号量做了微小的改动。函数 xSemaphoreGiveFromISR()不能用于在中断中释放互斥信号量,因为互斥信号量涉及到优先级继承的问题,而中断不属于任务,没法处理中断优先级继承。

1.5 获取信号量

​ 同释放信号量的 API 函数一样,不管是二值信号量、计数型信号量还是互斥信号量,获取信号量也有两个函数,如表 14.2.5.1 所示:

img

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

​ 获取信号量的过程其实就是读取队列的过程,只是这里并不是为了读取队列中的消息。如果队列为空并且阻塞时间为 0 的话,就立即返回 errQUEUE_EMPTY,表示队列满。如果队列为空并且阻塞时间不为 0 的话就将任务 添加到延时列表中。如果队列不为空的话就从队列中读取数据(获取信号量不执行这一步),数据读取完成以后还需要将队列结构体成员变量 uxMessagesWaiting 减一,然后解除某些因为入 队而阻塞的任务,最后返回 pdPASS 表示出对成功。互斥信号量涉及到优先级继承,处理方式 不同,后面讲解互斥信号量的时候在详细的讲解。

#define xSemaphoreTakeFromISR( xSemaphore, pxHigherPriorityTaskWoken ) xQueueReceiveFromISR( ( QueueHandle_t ) ( xSemaphore ), NULL, ( pxHigherPriorityTaskWoken ) )

​ 在中断中获取信号量真正使用的是函数 xQueueReceiveFromISR (),这个函数就是中断级 出队函数!当队列不为空的时候就拷贝队列中的数据(用于信号量的时候不需要这一步),然后 将队列结构体中的成员变量 uxMessagesWaiting 减一,如果有任务因为入队而阻塞的话就解除 阻塞态,当解除阻塞的任务拥有更高优先级的话就将参数pxHigherPriorityTaskWoken 设置为 pdTRUE,最后返回 pdPASS 表示出队成功。如果队列为空的话就直接返回 pdFAIL 表示出队失 败!这个函数还是很简单的。

2 计数型信号量
2.1 计数型信号量简介

​ 有些资料中也将计数型信号量叫做数值信号量,二值信号量相当于长度为 1 的队列,那么 计数型信号量就是长度大于 1 的队列。同二值信号量一样,用户不需要关心队列中存储了什么 数据,只需要关心队列是否为空即可。计数型信号量通常 用于如下两个场合:

1 、事件计数

​ 在这个场合中,每次事件发生的时候就在事件处理函数中释放信号量(增加信号量的计数值),其他任务会获取信号量(信号量计数值减一,信号量值就是队列结构体成员变量 uxMessagesWaiting来处理事件。在这种场合中创建的计数型信号量初始计数值为 0。

2 、资源管理

​ 在这个场合中,信号量值代表当前资源的可用数量。 一个任务要想获得资源的使用权,首先必须获取信号量,信号量获取成功以后信号量值就会减 一。当信号量值为 0 的时候说明没有资源了。当一个任务使用完资源以后一定要释放信号量, 释放信号量以后信号量值会加一。

2.2 创建计数型信号量

FreeRTOS 提供了两个计数型信号量创建函数,如表 14.4.2.1 所示

img

计数型信号量也是在队列的基础上实现的,所以需要调用函数 xQueueGenericCreate() 创 建 一 个 队 列 , 队 列 长 度 为 uxMaxCount , 对 列 项 长 度 为 queueSEMAPHORE_QUEUE_ITEM_LENGTH( 此 宏 为 0) , 队 列 的 类 型 为 queueQUEUE_TYPE_COUNTING_SEMAPHORE,表示是个计数型信号量。

#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )

​ 此函数用于创建一个计数型信号量,所需要的内存通过动态内存管理方法分配。此函数本质是一个宏,真正完成信号量创建的是函数 xQueueCreateCountingSemaphore()。

#define xSemaphoreCreateCountingStatic( uxMaxCount, uxInitialCount, pxSemaphoreBuffer ) xQueueCreateCountingSemaphoreStatic( ( uxMaxCount ), ( uxInitialCount ), ( pxSemaphoreBuffer ) )

​ 此函数也是用来创建计数型信号量的,使用此函数创建计数型信号量的时候所需要的内存 需要由用户分配。此函数也是一个宏,真正执行的是函数xQueueCreateCountingSemaphoreStatic(),

3 互斥信号量
3.1 互斥信号量简介

互斥信号量其实就是一个拥有优先级继承的二值信号量,在同步的应用中(任务与任务或中 断与任务之间的同步)二值信号量最适合。互斥信号量适合用于那些需要互斥访问的应用中。在 互斥访问中互斥信号量相当于一个钥匙,当任务想要使用资源的时候就必须先获得这个钥匙,当使用完资源以后就必须归还这个钥匙,这样其他的任务就可以拿着这个钥匙去使用资源。 互斥信号量使用和二值信号量相同的 API 操作函数,所以互斥信号量也可以设置阻塞时间, 不同于二值信号量的是互斥信号量具有优先级继承的特性。当一个互斥信号量正在被一个低优先级的任务使用,而此时有个高优先级的任务也尝试获取这个互斥信号量的话就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级,这个过程就是优先级继承。优先级继承尽可能的降低了高优先级任务处于阻塞态的时间,并且将已经出现的“优先级翻转”的影响降到最低。 优先级继承并不能完全的消除优先级翻转,它只是尽可能的降低优先级翻转带来的影响。 硬实时应用应该在设计之初就要避免优先级翻转的发生。互斥信号量不能用于中断服务函数中, 原因如下:

● 互斥信号量有优先级继承的机制,所以只能用在任务中,不能用于中断服务函数。

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

3.2 创建互斥信号量

FreeRTOS 提供了两个互斥信号量创建函数,如表 14.8.2.1 所示

img

#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )

此函数用于创建一个互斥信号量,所需要的内存通过动态内存管理方法分配。此函数本质 是一个宏。

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

此函数也是创建互斥信号量的,只不过使用此函数创建互斥信号量的话信号量所需要的 RAM 需要由用户来分配,此函数是个宏。

3.3 释放互斥信号量

释 放 互 斥 信 号 量 的 时 候 和 二 值 信 号 量 、 计 数 型 信 号 量 一 样 , 都 是 用 的 函 数 xSemaphoreGive()(实际上完成信号量释放的是函数 xQueueGenericSend())。不过由于互斥信号量 涉及到优先级继承的问题,所以具体处理过程会有点区别。使用函数 xSemaphoreGive()释放信 号 量 最 重 要 的 一 步 就 是 将uxMessagesWaiting 加 一 , 而 这 一 步 就 是 通 过 函 数 prvCopyDataToQueue() 来 完 成 的 , 释 放 信 号 量 的 函 数 xQueueGenericSend() 会 调 用

prvCopyDataToQueue()。互斥信号量的优先级继承也是在函数 prvCopyDataToQueue()中完成的,

4 递归互斥信号量

​ 递归互斥信号量可以看作是一个特殊的互斥信号量,已经获取了互斥信号量的任务就不能 再次获取这个互斥信号量,但是递归互斥信号量不同,已经获取了递归互斥信号量的任务可以 再次获取这个递归互斥信号量,而且次数不限.一个任务使用函数 xSemaphoreTakeRecursive() 成功的获取了多少次递归互斥信号量就得使用函数 xSemaphoreGiveRecursive()释放多少次!比 如某个任务成功的获取了 5 次递归信号量,那么这个任务也得同样的释放 5 次递归信号量。 递归互斥信号量也有优先级继承的机制,所以当任务使用完递归互斥信号量以后一定要记 得释放。同互斥信号量一样,递归互斥信号量不能用在中断服务函数中。

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

● 中断服务函数不能设置阻塞时间。 要使用递归互斥信号量的话宏 configUSE_RECURSIVE_MUTEXES 必须为 1!

5 优先级翻转

​ 在使用二值信号量的时候会遇到很常见的一个问题:优先级翻转。优先级翻转在可剥夺内核中是非常常的,在实时系统中不允许出现这种现象,这样会破坏任务的预期顺序,可能会导致严重的后果。下图是一个优先级翻转的例子。

img

(1)任务H和任务M处于挂起状态,等待某一事件的发生,任务L正在运行。

(2)某一时刻任务L想要访问共享资源,在此之前它必须先获得对应资源的信号量。

(3)任务L获得信号量并开始使用该共享资源。

(4)由于任务H优先级高,它等待的时间发生后便剥夺了任务L的CPU使用权。

(5)任务H开始运行。

(6)任务H运行过程中也要使用任务L正在使用着的资源,由于该资源的信号量还被L占用着,任务H只能进入挂起状态,等待任务L释放该信号量。

(7)任务L继续运行。

(8)由于任务M优先级高于任务L,当任务M等待的事件发生后,任务M剥夺了任务L的CPU使用权。

(9)任务M处理该处理的事。

(10)任务M执行完毕后,将CPU使用权归还给任务L。

(11)任务L继续执行。

(12)最终任务L完成所有的工作并释放了信号量,到此为止,由于实时内核知道有个高优先级的任务正在等待这个信号量,故内核做任务切换。

(13)任务H得到该信号量并接着运行。

​ 在这种情况下,任务H的优先级实际上降到了任务L的优先级水平。因为任务H要一直等待任务L释放其占用的那个共享资源。由于任务M剥夺了任务L的CPU使用权,使得任务H的情况更加恶化,这样就相当于任务M的优先级高于任务H,导致优先级翻转。

传送门:
【freeRTOS】操作系统之一-任务调度
【freeRTOS】操作系统之二-队列
【freeRTOS】操作系统之三-信号量
【freeRTOS】操作系统之四-事件标志组
【freeRTOS】操作系统之五.-内存管理
【freeRTOS】操作系统之六-低功耗模式
【freeRTOS】操作系统之七-freeRtos移植

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

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

相关文章

计算机毕业设计springboot+vue+elementUI 广场舞团高校舞蹈社团管理系统

项目介绍 随着时代的发展,高校的社团发展完善,同时越来越多的年轻人喜欢广场舞以及各类舞蹈。为了让更多的年轻人能够有一个自己喜欢的爱好,并且有一个共同交流的平台。我们开发了本质的广场舞团,高校社团管理系统。通过本系统可…

代码随想录day59|503. 下一个更大元素 II|42. 接雨水|Golang

代码随想录day59 还剩下一天 目录 代码随想录day59 503. 下一个更大元素 II 42. 接雨水 双指针解法 动态规划解法 单调栈解法 503. 下一个更大元素 II func nextGreaterElements(nums []int) []int {n : len(nums)ans : make([]int,n,n)for i:0;i<len(ans);i{ans[i]…

ECCV2022 商汤 发布最大的表征学习预训练数据集OmniBenchmark解读

近些年&#xff0c;基于深度模型的表征学习算法在某些知识域上&#xff08;例如人脸、动物等&#xff09;取得了非常优异的成绩&#xff0c;然而由于现有数据集覆盖的视觉类别仍然比较有限&#xff0c;一个覆盖视觉类别足够广&#xff0c;且能够支持学习到适用于许多视觉类别的…

CRM的定义是什么?这么多CRM产品,CRM客户管理系统该如何选择?

在禽流感的助涨下&#xff0c;CRM被拉到了众矢之的。 为甚么这样说呢&#xff1f;绝大多数民营企业遭遇着巨大的存活压力&#xff0c;导致民营企业不得已展开结构调整和网络化。CRM作为一种专精的客人关系管理工作工具&#xff0c;再次受到追捧。 CRM具有客人管理工作、网络营…

JavaScript的DOM技术

JavaScript的DOM技术 文章目录JavaScript的DOM技术1.DOM简介1.1 DOM概念2.获取元素2.1 如何获取页面元素2.2 根据ID获取2.3 根据标签名获取2.4 根据标签名获取2.5 通过H5新增方法获取2.6 获取特殊元素3.事件基础3.1 事件概述3.2 事件三要素3.3 执行事件的步骤3.4 鼠标事件4.操作…

工程施工监理平台app开发 开创工程监理服务新理念

工程项目最容易让人焦头烂额&#xff0c;很难统揽全局&#xff0c;更无法将施工中的每一个问题都处理得当&#xff0c;工程施工监理平台app开发&#xff0c;从根本上解决了工程监管问题&#xff0c;成为工程施工监管方面的福音和好帮手。工程施工监理平台app开发是专注工程现场…

【单目3D目标检测】FCOS3D + PGD论文解析与代码复现

文章目录前言FCOS3D概述主要创新点主要框架结构回归目标损失函数推理过程2D引导的多层3D预测2D高斯分布的3D中心度实验设置源码复现PGD概述主要创新点深度估计主要框架结构创新点一&#xff1a;概率表示的不确定性建模DPD_PDP​创新点二&#xff1a;透视几何体的深度传播DGD_GD…

删除或者移动文件/文件夹时,提示:文件/文件夹正在使用

问题 有时候我们在移动或者删除文件/文件夹时&#xff0c;系统会提示“文件正在使用”。 操作无法完成&#xff0c;因为其中的文件夹或者文件已经在另一程序中打开 请关闭该文件夹或文件&#xff0c;然后重试。 这是因为文件夹中的某个文件被打开了&#xff0c;或者该文件或文件…

VR云游带你玩转智慧文旅,解决景区营销痛点

有人说防控措施正在逐步放开&#xff0c;大家那颗热爱旅游的心是不是正在蠢蠢欲动了呢&#xff1f;不要急&#xff0c;先来一波VR云游助助兴吧&#xff01; 朝游青山暮游雪&#xff0c; 上午还在十里长湖、八里磨山&#xff0c; 下午便在毓秀金陵、钟山龙蟠&#xff0c; 看大…

[附源码]SSM计算机毕业设计超市订单管理系统JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

前端使用nginx部署项目到服务器

目录 一、下载nginx 二、启动 三、验证 四、windows的操作指南 五、目录介绍 六、部署 一、下载nginx 下载地址&#xff1a;https://nginx.org/en/download.html 二、启动 两种方法&#xff1a; 1&#xff09; 直接双击该目录下的"nginx.exe"&#xff0c;即…

设置代理服务器

挂载代理 前提&#xff1a; 同一个局域网&#xff0c;有一个IP白名单可以访问网络&#xff08;win10&#xff09;。想实现其他机器&#xff08;linux&#xff09;共同访问。 先将机器(linux)设置固定IP&#xff0c;同一网关下。静态IP设置&#xff0c;保证能PING通。将win机器…

雪佛兰畅巡新能源电动汽车CANBUS总线适配及汽车远程控制车联网系统

随着智能交通的发展&#xff0c;中国作为全球最大的汽车市场&#xff0c;车联网的市场容量巨大&#xff0c;国内车联网摸索了很多年&#xff0c;前装也还是属于霸屏&#xff0c;提供信息娱乐等初期阶段。互联网对汽车行业确实推动了不少&#xff0c;汽车公司也与互联网融合做过…

淘宝天猫CTO若海:沉浸式的消费体验是下一步发力方向

​每年双 11 开卖的那一刻&#xff0c;千万用户同时在线下单&#xff0c;那个瞬间服务器的压力是平时流量的数百倍&#xff0c;淘宝系统是否能够稳定支撑&#xff0c;是每年所有人关注的热点话题。 时至今日&#xff0c;丝般顺滑已经逐渐成为稳态。从今年开始&#xff0c;双 11…

使用minio进行文件存储

title: 使用Minio存储文件对象 一. Docker拉取镜像&#xff08;确保自己的服务器已经安装Docker&#xff09; docker pull minio/minio二. 启动一个miniio容器 docker run --name minio -p 9090:9000 -p 9999:9999 -d \ --restartalways -e \ "MINIO_ROOT_USERminio&qu…

“码二代”从喜欢益智游戏到找最短路线,编程思维是如何培养的?

前言 1842年&#xff0c;“数字女王”的阿达洛芙莱斯&#xff08;Ada Lovelace&#xff09;编写了历史上首款电脑程序&#xff0c;至今已有200多年的历史。 &#xff08;文末送读者福利&#xff09; 在这个特别的日子里&#xff0c;我们要为大家介绍的是一个来自小小“码二代…

线性表详细讲述(带图)

文章目录线性表---顺序表和链表1.线性表2.顺序表2.1概念2.2 静态顺序表与动态顺序表2.3接口的实现2.3.1顺表的初始化2.3.2扩容2.3.3顺序表尾插2.3.4顺序表的尾删2.3.5顺序表的头插2.3.6顺序表的头删2.3.7顺序表的查找2.3.8顺序表的任意位置插入2.3.9顺序表的任意位置删除2.3.10…

[附源码]java毕业设计民宿网站管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【DL】linux服务器上安装Anaconda3

1.本地连接远程服务器 使用MobaXterm连接远程服务器 2.下载Anaconda3安装包 安装包下载地址 https://www.anaconda.com/ 因为我们要在linux服务器上安装,因此选择linux安装包 下载完成以后,将安装包拖进服务器 3.安装Anaconda3 打开终端,输入以下命令,目的是赋权限 c…

Vue路由

参考文献&#xff1a;Vue中的路由 一、路由理解&#xff1a; 一个路由就是一组映射关系&#xff08;key&#xff0c;value&#xff09;&#xff0c;多个路由需要路由器&#xff08;router&#xff09;进行管理。其中key是路径&#xff0c;value是组件。作用&#xff1a;设定访…