FreeRTOS(二)

第一部分 信号量

(一)信号量的本质

首先我们先来看队列的结构体,我们不难发现队列结构体中说到有个联合体在用于队列时,使用Queue,在用于信号量时,使用XSemaphore。后面又说到了一些对列的类型,顺着这条线索我们来说说信号量。

细说上面的联合体

其实我们就不难发现,队列就是上述结构体+一个环形存储区,而信号量是一个特殊的队列,他只有队列的头部,并没有后面的环形存储区,因为信号量只负责信息传递,并不传递数据。

类似这样一个结构体。

(二)简介

信号量是一种任务间通信的机制,用来实现任务之间同步或临界资源的互斥访问。

是一个非负整数。当这个值加一:表明有一个资源被释放;当这个值减一:表明有一个资源被占有,当减到0时,表明没有任何资源,这时所有任务没有办法再获取到信号量,处于阻塞状态。

1、分类
(1)二值信号量

所谓二值信号量其实就是一个队列长度为1,没有数据存储器的队列,而二值则表示计数值uxMessagesWaiting只有0和1两种状态(就是队列空与队列满两种情况),uxMessagesWaiting在队列中表示队列中现有消息数量,而在信号量中则表示信号量的数量。

uxMessagesWaiting为0表示:信号量资源被获取了. uxMessagesWaiting为1表示:信号量资源被释放了 把这种只有 0 和 1 两种情况的信号量称之为二值信号量。

a.特点:信号量最大是1。

b.用于对临界资源的访问,也可以用来同步。比如之前我们在linux里面所说的按顺序打印ABC

这个就可以使用到二值信号量来同步三个资源的循环打印。

由于二值信号量就是特殊的队列,其实它的运转机制就是利用了队列的阻塞机,从而达到实现任务之间的同步与互斥(有优先级反转的缺陷,这个后面会介绍)。

(2)计数信号量

a.特点:用于对事件的计数和资源的管理

事件的计数:当信号量⽤于对事件计数时,信号量的值表示未被处理的事件数。其过程为:当中断或是任务中某个事件发⽣时,就会释放⼀个信号量(信号量计数值+1),当事件被处理时,信号量计数值-1。

资源管理:信号量的计数值表示系统中可⽤资源的数量。但是任务必须先获取到信号量才能访问资 源。

b.例子:停车场的空车位:最开始空车位为最大值,停一辆车则空车位资源数就减一,出去一辆空车位就加一,当全部停满时,在有车来停则停车失败可以选择等待(等待有空车位),当空车位为最大值时则不能再继续出车(因为停车场已经没有车了)。

2、常用API函数
(1)二值信号量

这里只介绍目前我了解的

接口函数函数功能
xSemaphoreCreateBinary()(动态)创建一个二值信号量
xSemaphoreTake()获取信号量
xSemaphoreGive()释放信号量
xSemaphoreDelete()删除信号量
SemaphoreHandle_t信号量句柄
a.信号量句柄

从这里我们也能发现信号量其实就是一个特殊的队列。

b.创建一个二值信号量

实际是调用了这个函数

c.获取一个信号量

获取信号量其实与队列的出队基本差不多的 ①获取信号量资源数uxMessagesWaiting(在二值信号量中只能是0/1,代表信号量为空或满) ②判断信号量是否有资源uxSemaphoreCount >0 (有资源才能被获取) ③如果信号量有资源,uxSemaphoreCount 计数值减一

d.释放一个信号量

实际调用的是这个函数

1.判断信号量计数值是否达最大值, pxQueue->uxMessagesWaiting < pxQueue->uxLength ,(在创建二值信号量中,uxLength 赋值为1,则信号量计数值的最大值就为1,也就是说计数值要等于0才能释放信号) 2.若满足信号量释放条件,则计数值(uxMessagesWaiting )加1,并不会拷贝数据,此时计数值为1表示信号量有资源,如果有因为获取信号量而阻塞的任务,则需要将其唤醒(从xTasksWaitingToReceive列表中移除,将任务挂入就绪列表)

e.删除信号量
(2)计数信号量

这里只介绍目前我了解的

接口函数函数功能
xSemaphoreCreateCounting()(动态)创建一个计数值信号量
xSemaphoreTake()获取信号量
xSemaphoreGive()释放信号量
xSemaphoreDelete()删除信号量
SemaphoreHandle_t信号量句柄

其实创建计数值信号量函数很简单,里面还是调用了队列创建函数xQueueGenericCreate(),创建一个长度为uxMaxCount,消息大小为0的特殊队列即计数值信号量,当然创建成功后,将计数值初值赋值成uxInitialCount,就是这么简单,然后其他获取、释放信号量等操作与二值信号量一模一样。

(三)互斥量

1、基本介绍

(1)概念:互斥量,⼜称互斥信号量,是⼀种特殊的⼆值信号量。

(2)状态:开锁 或 闭锁。 当线程持有它后,互斥量处于闭锁状态,由这个线程获得它的所有权。相反,当这个线程释放这个锁后,将对互斥量解锁,失去它的所有权。

(3)特点:互斥量所有权、递归访问、防⽌优先级翻转,解决了二值信号量存在优先级翻转的缺点,可以优先级继承。

2、优先级反转
2.1简介

低优先级的任务霸占CPU资源,导致高优先级任务无法运行的情况。低优先级的任务持有一个被高优先级任务所需要的共享资源(低优先级给共享资源上锁,高优先级需要等待低优先级解锁共享资源,而在这期间正好一个中等优先级的任务打断低优先级任务去执行,则低优先级任务迟迟不能运行则不能给共享资源解锁,导致高优先级要一直等待(阻塞),而中等优先级的任务正在运行(逍遥法外))。我们把这种高优先级任务无法运行而低优先级的任务可以运行的现象 称之为------优先级翻转。

优先级翻转带来的危害:续接上述案列,任务H等待资源⽽阻塞,任务L还在占⽤资源,此时任务M就绪,将抢占CPU,最终任务H等待执行的时间 = 任务M执行完 + 任务L释放资源。如果,有多个任务打断L,那么任务H的等待时间将会变的更长。

2.2代码演示

解决方式就是优先级继承,不是说低优先级任务无法执行,就在高优先级任务进入阻塞之前将低优先级任务的优先级提升至与高优先级一致,这样等高优先级任务进入阻塞之后,低优先级任务就能继承高优先级任务的优先级,这样低优先级任务就能尽快执行(从而解锁,让高优先级能够获取锁)。

3、优先级继承

优先级继承:暂时提高某个占有某种资源的低优先级任务的优先级,使之与在所有等待该资源的任务中优先级最高那个任务的优先级相等,而当这个低优先级任务执行完毕释放该资源时,优先级重新回到初始设定值。

当高优先级任务获取锁进入阻塞之前,会将低优先级任务的优先级提升至与高优先级任务一样,则等高优先级任务C进入阻塞之后,则低优先级任务A继承任务C的优先级,则任务A立马执行(任务B没机会执行),则任务A继续执行就能尽快开锁(释放信号量),这样就能极大的减少优先级反转带来的影响。

4、互斥量所有权

当一个线程持有互斥量时,其他线程将不能够对它进行开锁或持有它,持有该互斥量的线程也能够再次获得这个锁而不被挂起。

多线程环境下往往存在多个线程竞争同一临界资源的应用场景,互斥量可被用于对临界资源的保护从而实现独占式访问。

所以根据这个特征也可以解决上述优先级翻转的问题。

!!!注意

互斥信号量不能用于中断服务函数中,原因如下: (1) 互斥信号量有任务优先级继承的机制,但是中断不是任务,没有任务优先级,所以互斥信号量只能用与任务中,不能用于中断服务函数。 (2) 中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态

中断服务函数(Interrupt Service Routine,ISR)是一种特殊的函数,在处理硬件中断时会被调用。由于中断服务函数是在中断上下文中执行的,其执行期间必须尽可能地短小和高效,以确保不会影响系统的实时性和稳定性。因此,在中断服务函数中通常应该避免使用会导致阻塞的操作,比如等待互斥信号量而设置阻塞时间进入阻塞态。

主要原因包括:

  1. 实时性要求: 中断服务函数需要尽快完成对中断的处理,以确保系统能够及时响应硬件中断。如果在中断服务函数中设置阻塞时间并进入阻塞态,会导致无法满足实时性要求,可能造成系统性能下降或功能异常。

  2. 上下文切换: 在中断服务函数中执行阻塞操作可能引起上下文切换的开销。当中断服务函数被阻塞时,操作系统可能需要切换到其他任务来继续执行,这会增加系统的开销并可能引入不确定性。

  3. 死锁风险: 在中断服务函数中使用互斥信号量并设置阻塞时间会增加系统发生死锁的风险。如果其他任务持有需要的互斥信号量并且不释放,那么中断服务函数将永远无法获取该信号量,导致死锁。

5、互斥信号量解析
5.1 API函数
接口函数函数功能
xSemaphoreCreateMutex()(动态)创建一个互斥信号量
xSemaphoreTake()获取信号量
xSemaphoreGive()释放信号量
xSemaphoreDelete()删除信号量
SemaphoreHandle_t

互斥信号量句柄

在创建互斥信号量时候会释放一次信号量,表示一开始就有资源,而二值信号量则不会释放。

接下来就是看在什么时候进行优先级继承,什么时候解除优先级继承?

进行优先级继承的时候肯定是高优先级任务去获取信号量失败然后让低优先级任务继承它的优先级。 进行解除优先级继承的时候肯定是低优先级释放信号量,此时它的任务完成需要恢复成之前的优先级。

所以优先级继承发生在获取信号量,解除优先级继承发生在释放信号量。

以上两张图是我截别人的,在这块还是需要花大功夫研究一下里面继承的机制。

6、递归访问互斥信号量
6.1 API函数
接口函数函数功能
xSemaphoreCreateRecursiveMutex()(动态)创建一个递归互斥信号量
xSemaphoreTakeRecursive()获取信号量
xSemaphoreGiveRecursive()释放信号量
xSemaphoreDelete()删除信号量
6.2为什么互斥信号量可以递归

互斥锁支持递归访问是为了解决同一任务需要多次获取同一个互斥锁的情况。当一个任务已经获取了互斥锁,并且在持有锁的状态下再次请求该锁时,如果互斥锁支持递归访问,那么这个任务可以再次成功获取该锁,而不会因为自己已经持有锁而被阻塞。

支持递归访问的互斥锁通过记录任务对锁的获取次数来实现递归访问。每当任务成功获取锁时,记录获取锁的次数;每当任务释放锁时,相应地减少获取锁的次数。只有当获取锁的次数归零时,其他任务才能成功获取该锁。

递归访问的互斥锁在某些情况下非常有用,比如在复杂的递归函数中可能需要多次获取同一个锁,或者在对象导向的程序设计中可能需要在对象的不同方法中多次获取同一个锁。

6.3 为什么普通信号量不能递归

信号量是一种用于任务同步和互斥的机制,与互斥锁不同,信号量并不支持递归访问。这是因为信号量的目的是控制对资源的访问权限,而不是为了提供递归锁定的功能。

在信号量中,当一个任务获取信号量时,信号量的计数会相应地减少;当任务释放信号量时,计数会增加。任务获取信号量后,其他任务将无法再次获取相同的信号量,直到原任务释放信号量为止。这种机制确保了资源的独占性和排他性。

如果信号量支持递归访问,就会破坏信号量所设计的用途和原理。递归访问会导致信号量的计数出现不可预测的变化,可能引发死锁或其他意想不到的问题。因此,为了保持信号量的正确性和一致性,通常不建议在信号量上实现递归访问的功能。如果需要递归锁定的功能,应该使用互斥锁来实现。

(四)一些实例

1、生产者消费者

2、停车场问题

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

简述C语言文件操作

&#x1f308; 个人主页&#xff1a;白子寰 &#x1f525; 分类专栏&#xff1a;魔法指针&#xff0c;进阶C&#xff0c;C语言&#xff0c;C语言题集&#xff0c;C语言实现游戏&#x1f448; 希望得到您的订阅和支持~ &#x1f4a1; 坚持创作博文(平均质量分79)&#xff0c;分享…

逻辑运算符、#define易错点

文章目录 逻辑运算符#define易错点 一、逻辑运算符 #include<stdio.h> #define PERIOD . int main() {char ch;int charcount0;while((chgetchar())!PERIOD){if(ch!"&&ch!\)charcount;}printf("There are %d non-quote characters.\n",charcount…

算法---动态规划练习-1(三步问题)

三步问题 1. 题目解析2. 讲解算法原理3. 编写代码 1. 题目解析 题目地址&#xff1a;三步问题 2. 讲解算法原理 1. 定义一个常量MOD为10^97&#xff0c;用于取模运算。 2. 创建一个长度为n3的数组dp&#xff0c;用于存储计算过程中的中间结果。数组的下标表示台阶的级数&…

扩展一下BenchmarkSQL,新增支持ASE/HANA/DB2/SQLServer,可以随便用了

1 背景 提到数据库的性能,自然就避不开性能测试。有专用于测试OLTP的,也有偏重于OLAP的。本文介绍的BenchmarkSQL就属于测试OLTP中的一个,基于TPCC的。网上有很多介绍TPC*的相关测试的文章,大家可以自行脑补。而PostgreSQL自带的pgbench是属于TPCC的前一个基准测试程序,偏…

【vue核心技术实战精讲】1.3 - 1.6 VUE 指令 (上)

前言 上节,我们学习了 Vue的起步 和 插值表达式 本节内容 Vue指令之v-text 和 v-htmlVue指令之v-if 和 v-showVue指令之v-bind绑定Vue指令之v-on事件处理 1、v-text 和 v-html {{}} 和v-text的作用是一样的 都是插入值,直接渲染 ≈ innerTextv-html既能插入值 又能插入标签…

Linux下安装redis

1、redis的编译环境 Redis是C语言开发的&#xff0c;安装redis需要先去官网下载源码进行编译&#xff0c;编译需要依赖于GCC编译环境&#xff0c;如果CentOS上没有安装gcc编译环境&#xff0c;需要提前安装&#xff0c;安装命令如下:&#xff08;这里我们使用root用户处理这些…

linux 区别:mount 一个目录到另外一个目录,目录软链接 (*)

Linux命令200例&#xff1a;mount将文件系统挂载到指定目录下&#xff08;常用&#xff09; https://blog.csdn.net/qq_21891743/article/details/132220283 Linux磁盘卸载 https://blog.csdn.net/Mcy7ycM/article/details/124347504 能否通俗易懂&#xff0c;深入浅出地解释…

Zabbix使用TimescaleDB数据库

一、前言 Zabbix 6.0 已发布很久&#xff0c;下个季度7.0应该会正式发布&#xff0c;但6.0也有许多新功能和新特性&#xff0c;这里介绍 6.0 配置 TimescaleDB&#xff0c;此安装配置方法可基本通用与其他版本。 二、TimescaleDB TimescaleDB 基于 PostgreSQL 数据库打造的一…

MySQL数据库 - 单表查询(三)

一个不知名大学生&#xff0c;江湖人称菜狗 original author: Jacky Li Email : 3435673055qq.com Time of completion&#xff1a;2024.03.24 Last edited: 2024.03.24 目录 第1关&#xff1a;对查询结果进行排序 任务描述 相关知识 对查询结果排序 指定排序方向 编程要…

需求分析的过程

需求分析的工具 ominGraffle/Visio Gliffy ProcessOn RSA(UML) PPT/WORD 手绘 需求所需要的工件&#xff1a; 系统上下文、用例模型、质量限制 1.系统上下文的工件 2.用例模型工件&#xff08;什么功能&#xff09; 3.质量和限制 质量&#xff1a;管理10个小动物&#xff0c;…

注册中心的基础知识

什么是注册中心 当服务启动时,将服务信息服务名称/IP/端口写入注册中心.注册中心接收服务端信息时保存服务信息,并且维护服务列表数据当服务消费者启动时会通过IP:端口(注册中心)远程链接注册中心. 获取服务列表信息.缓存到本地 当消费者调用服务时,查找缓存到本地的服务列表…

Druid连接池的能力介绍与使用方法

Druid连接池的能力介绍与使用方法 本文将介绍druid连接池的能力&#xff1a;监控sql调用数据&#xff08;慢sql、调用量、异常堆栈&#xff09;、防止sql注入和数据库密码加密。 1. Druid连接池简介 Alibaba Druid官网使用手册里是这样介绍的&#xff1a;Druid连接池是阿里巴…

【电气安全】ASCP电气防火限流式保护器/末端回路线路保护

为什么要使用电气防火限流式保护器&#xff1f; 应急管理部消防救援局统计&#xff0c;在造成电气火灾事故的原因中&#xff0c;最为主要的当为末端线路短路&#xff0c;在电气火灾事故中占比高达70%以上。如何效预防末端线路短路引发的电气火灾事故&#xff1f; 现阶段最为常…

P2926 [USACO08DEC] Patting Heads S题解

题目 今天是贝茜的生日&#xff0c;为了庆祝自己的生日&#xff0c;贝茜邀你来玩一个游戏。 贝茜让N (1≤N≤) 头奶牛坐成一个圈。除了1号与N号奶牛外&#xff0c;i号奶牛与i−1号和i1号奶牛相邻。N号奶牛与1号奶牛相邻。农夫约翰用很多纸条装满了一个桶&#xff0c;每一张包…

按列值分组并横向全连接

按列值分组并横向全连接 原效果 处理效果 import pandas as pddef segmentation_col(df ,col_name):# 按列的值分组list_type df[col_name].unique()df_list []for item in list_type:df_list.append(df[df[col_name] item])# 并横向连接# 判断是否能连接if len(df_list) …

Java 自定义线程池实现

自定义线程池 简介任务图示阻塞队列 BlockingQueue<T>ReentrantLock代码 线程池 ThreadPool工作线程类 Worker 拒绝策略接口代码测试类 TestThreadPool为什么需要j i&#xff1f;&#xff08;lambad表达式相关&#xff09; 测试结果拒绝策略&#xff1a;让调用者自己执行…

【计算机网络篇】数据链路层(3)差错检测

文章目录 &#x1f95a;误码&#x1f354;两种常见的检错技术⭐奇偶校验⭐循环冗余校验&#x1f388;例子 &#x1f95a;误码 误码首先介绍误码的相关概念 &#x1f354;两种常见的检错技术 ⭐奇偶校验 奇校验是在待发送的数据后面添加1个校验位&#xff0c;使得添加该校验…

谷粒商城 分布式组件

1.成功启动人人开源前端项目和后端项目联调 RefreshScope //动态从配置中心获取配置

LeetCode Python - 71. 简化路径

目录 题目描述解法运行结果 题目描述 给你一个字符串 path &#xff0c;表示指向某一文件或目录的 Unix 风格 绝对路径 &#xff08;以 ‘/’ 开头&#xff09;&#xff0c;请你将其转化为更加简洁的规范路径。 在 Unix 风格的文件系统中&#xff0c;一个点&#xff08;.&…

数学(算法竞赛、蓝桥杯)--快速幂

1、B站视频链接&#xff1a;G01 快速幂_哔哩哔哩_bilibili 题目链接&#xff1a;P1226 【模板】快速幂 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) #include <bits/stdc.h> using namespace std; typedef long long LL; int a,b,p; int quickpow(LL a,int n,int p){…