【学习FreeRTOS】第14章——FreeRTOS信号量

news2024/12/29 1:38:55

1.信号量的简介

信号量是一种解决同步问题的机制,可以实现对共享资源的有序访问。

  • 信号量:用于传递状态(区别于队列传递消息)

  • 信号量的计数值都有限制:限定最大值。

  • 如果最大值被限定为1,那么它就是二值信号量;如果最大值不是1,它就是计数型信号量
    在这里插入图片描述

  • 当计数值大于0,代表有信号量资源

  • 当释放信号量,信号量计数值(资源数)加一

  • 当获取信号量,信号量计数值(资源数)减一
    在这里插入图片描述

2.二值信号量

二值信号量的本质是一个队列长度为 1 的队列 ,该队列就只有空和满两种情况,这就是二值。二值信号量通常用于互斥访问或任务同步, 与互斥信号量比较类似,但是二值信号量有可能会导致优先级翻转的问题 ,所以二值信号量更适合用于同步
在这里插入图片描述
使用二值信号量的过程:创建二值信号量->释放二值信号量->获取二值信号量

  • xSemaphoreCreateBinary():使用动态方式创建二值信号量
  • xSemaphoreCreateBinaryStatic():使用静态方式创建二值信号量
  • xSemaphoreGive():释放信号量
  • xSemaphoreGiveFromlSR():在中断中释放信号量
  • xSemaphoreTake():获取信号量
  • xSemaphoreTakeFromISR():在中断中获取信号量
  • uxSemaphoreGetCount():获取信号量的计数值

2.1.创建二值信号量(动态)xSemaphoreCreateBinary()

SemaphoreHandle_t   xSemaphoreCreateBinary(void) 
#define   			xSemaphoreCreateBinary(void)
					xQueueGenericCreate(1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE )

#define semSEMAPHORE_QUEUE_ITEM_LENGTH      ( ( uint8_t ) 0U )
#define queueQUEUE_TYPE_BASE                ( ( uint8_t ) 0U )	/* 队列 */
#define queueQUEUE_TYPE_SET                 ( ( uint8_t ) 0U )	/* 队列集 */
#define queueQUEUE_TYPE_MUTEX               ( ( uint8_t ) 1U )	/* 互斥信号量 */
#define queueQUEUE_TYPE_COUNTING_SEMAPHORE  ( ( uint8_t ) 2U )	/* 计数型信号量 */
#define queueQUEUE_TYPE_BINARY_SEMAPHORE    ( ( uint8_t ) 3U )	/* 二值信号量 */
#define queueQUEUE_TYPE_RECURSIVE_MUTEX     ( ( uint8_t ) 4U )	/* 递归互斥信号量 */
  • 形参:无
  • 返回值:NULL,创建失败;其他值,创建成功返回二值信号量的句柄

2.2.释放信号量(通用)xSemaphoreGive()

BaseType_t	xSemaphoreGive(xSemaphore) 
#define   	xSemaphoreGive(xSemaphore)	
			xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK)
#define   	semGIVE_BLOCK_TIME	( ( TickType_t ) 0U )
  • 形参xSemaphore:要释放的信号量句柄
  • 返回值:pdPASS,释放信号量成功;errQUEUE_FULL,释放信号量失败
  • 注意:释放信号量相当于写队列,但是当计数最大时无法阻塞,返回错误

2.3.获取信号量(通用)xSemaphoreTake()

BaseType_t	xSemaphoreTake( xSemaphore, xBlockTime ) 
#define 	xSemaphoreTake( xSemaphore, xBlockTime )    
			xQueueSemaphoreTake( ( xSemaphore ), ( xBlockTime ) )
BaseType_t 	xQueueSemaphoreTake( 	QueueHandle_t xQueue,
                                	TickType_t xTicksToWait )
  • 形参xSemaphore:要获取的信号量句柄
  • 形参xBlockTime:阻塞时间
  • 返回值:pdTRUE,获取信号量成功;pdFALSE,超时,获取信号量失败
  • 注意:获取信号量相当于读队列,同样可以阻塞

2.4.获取信号量的计数值(通用)uxSemaphoreGetCount()

UBaseType_t 	uxSemaphoreGetCount(xSemaphore)
#define 		uxSemaphoreGetCount(xSemaphore) 
				uxQueueMessagesWaiting( ( QueueHandle_t ) ( xSemaphore ) )
  • 形参xSemaphore:信号量句柄
  • 返回值:整数,当前信号量计数值的大小

2.5.二值信号量实验

  • 实验目的:学习 FreeRTOS 的二值信号量相关API函数的使用
  • 实验设计:将设计三个任务:start_task、task1、task2
    start_task-用来创建task1和task2任务
    task1-用于按键扫描,当检测到按键KEY0被按下时,释放二值信号量
    task2-获取二值信号量,当成功获取后打印提示信息
    在这里插入图片描述

3.计数型信号量

计数型信号量相当于队列长度大于1 的队列,因此计数型信号量能够容纳多个资源,这在计数型信号量被创建的时候确定的计数型信号量适用场合:

  • 事件计数:当每次事件发生后,在事件处理函数中释放计数型信号量(计数值+1),其他任务
    会获取计数型信号量(计数值-1) ,这种场合一般在创建时将初始计数值设置为 0
  • 资源管理:信号量表示有效的资源数目。任务必须先获取信号量(信号量计数值-1 )才能获取资源控制权。当计数值减为零时表示没有的资源。当任务使用完资源后,必须释放信号量(信号量计数值+1)。信号量创建时计数值应等于最大资源数目

API函数:

  • xSemaphoreCreateCounting() :使用动态方式创建计数信号量
  • xSemaphoreCreateCountingStatic() :使用静态方式创建计数信号量
  • xSemaphoreGive():释放信号量
  • xSemaphoreGiveFromlSR():在中断中释放信号量
  • xSemaphoreTake():获取信号量
  • xSemaphoreTakeFromISR():在中断中获取信号量
  • uxSemaphoreGetCount():获取信号量的计数值

3.1.创建计数信号量(动态)xSemaphoreCreateCounting()

#define	xSemaphoreCreateCounting(  uxMaxCount  ,  uxInitialCount  )
		xQueueCreateCountingSemaphore( (  uxMaxCount  ) , (  uxInitialCount  ) ) 
  • 形参uxMaxCount:计数值的最大值限定
  • 形参uxInitialCount:计数值的初始值
  • 返回值:NULL,创建失败;其他值,创建成功返回计数型信号量的句柄

3.2.计数型信号量实验

  • 实验目的:学习 FreeRTOS 的计数型信号量相关API函数的使用
  • 实验设计:将设计三个任务:start_task、task1、task2
    start_task-用来创建task1和task2任务
    task1-用于按键扫描,当检测到按键KEY0被按下时,释放计数型信号量
    task2-每过一秒获取一次计数型信号量,当成功获取后打印信号量计数值
    在这里插入图片描述

4.优先级翻转简介

优先级翻转:高优先级的任务反而慢执行,低优先级的任务反而优先执行
优先级翻转在抢占式内核中是非常常见的,但是在实时操作系统中是不允许出现优先级翻转的,因为优先级翻转会破坏任务的预期顺序,可能会导致未知的严重后果。 在使用二值信号量的时候,经常会遇到优先级翻转的问题。
在这里插入图片描述
解释:高优先级任务被低优先级任务阻塞,导致高优先级任务迟迟得不到调度。但其他中等优先级的任务却能抢到CPU资源。从现象上看,就像是中优先级的任务比高优先级任务具有更高的优先权(即优先级翻转)

5.互斥信号量

互斥信号量其实就是一个拥有优先级继承的二值信号量,在同步的应用中二值信号量最适合。互斥信号量适合用于那些需要互斥访问的应用中!
优先级继承:当一个互斥信号量正在被一个低优先级的任务持有时, 如果此时有个高优先级的任务也尝试获取这个互斥信号量,那么这个高优先级的任务就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级。
在这里插入图片描述
此时任务H的阻塞时间仅仅是任务L 的执行时间,将优先级翻转的危害降到了最低(注意没有消除)
优先级继承并不能完全的消除优先级翻转的问题,它只是尽可能的降低优先级翻转带来的影响 注意,互斥信号量不能用于中断服务函数中,原因如下:

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

API函数:

  • xSemaphoreCreateMutex() :使用动态方式创建互斥信号量
  • xSemaphoreCreateMutexStatic() :使用静态方式创建互斥信号量
  • xSemaphoreGive():释放信号量
  • xSemaphoreTake():获取信号量
  • uxSemaphoreGetCount():获取信号量的计数值

【注意:使用互斥信号量:首先将宏configUSE_MUTEXES置一】

5.1.使用动态方式创建互斥信号量xSemaphoreCreateMutex()

#define   xSemaphoreCreateMutex(void)      xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
  • 返回值:NULL,创建失败;其他值,创建成功返回计数型信号量的句柄
  • 注意:创建互斥信号量时,会主动释放一次信号量

6.递归互斥信号量

递归互斥信号量可以看作是特殊的互斥信号量,与互斥信号量不同的是,递归互斥信号量在被获取后,可以被其持有者重复获取,当然递归互斥信号量的持有者需要释放递归互斥信号量与之获取递归互斥信号量相同的次数,递归互斥信号量才算被释放。
递归互斥信号量与互斥信号量一样,也具备优先级继承机制,因此也不能在中断服务函数中使用递归互斥信号量。

7.总结对比:队列、二值信号量、数值信号量、互斥信号量

二值信号量数值信号量互斥信号量总结
创建xSemaphoreCreateBinary()
xSemaphoreCreateBinaryStatic()
xSemaphoreCreateCounting()
xSemaphoreCreateCountingStatic()
xSemaphoreCreateMutex()
xSemaphoreCreateMutexStatic()
创建函数不同
底层均为队列创建函数
释放xSemaphoreGive()
xSemaphoreCreateBinaryStatic()
xSemaphoreGive()
xSemaphoreCreateBinaryStatic()
xSemaphoreGive()
释放函数相同
互斥信号量无中断释放
获取xSemaphoreTake()
xSemaphoreTakeFromISR()
xSemaphoreTake()
xSemaphoreTakeFromISR()
xSemaphoreTake()获取函数相同
互斥信号量无中断获取
取值uxSemaphoreGetCount()uxSemaphoreGetCount()uxSemaphoreGetCount()获取值函数相同
  • 二值信号量、计数信号量、互斥信号量的创建函数不同,但是释放和获取均相同,此外注意,互斥信号量在中断中无法使用,所有没有中断中释放/获取信号量
  • 二值信号量、计数信号量、互斥信号量的创建函数不同,但是底层调用的都是同一个API(队列创建函数),只是内部机制不同、某些参数不同,创建的信号量储存结构只有结构体,没有队列项,依靠变量uxMessagesWaiting储存信号量信息
  • 二值信号量、计数信号量、互斥信号量的释放信号量通用,底层调用与队列写入函数相同,只是参数不同
  • 二值信号量、计数信号量、互斥信号量的获取信号量通用,底层调用与队列写入函数不相同(类似),无buf参数

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

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

相关文章

Kaggle回归问题Mercedes——Benz Greener Manufacturing

目录 前言1 题目介绍2 数据清洗3 数据可视化分析4 模型训练5 源码 前言 这是我在大三选修课的课程设计,内容参考了Kaggle上高赞的代码,有详细批注,整体比较基础,结构相对完整,便于初学者学习。这个是一个回归问题&…

BLE ch582 广播数据格式

BLE 蓝牙数据广播格式{6个字节(蓝牙设备MAC 地址)AD structure…AD structure N } 37字节 AD structure 长度类型内容 修改被扫描状态(被发现状态)的name:take 被扫描状态的name: take 链接后的状态变成;变成广播态;name: BLE…

Go 语言的实战案例 | 青训营

Powered by:NEFU AB-IN 文章目录 Go 语言的实战案例 | 青训营 Go补充简介猜数游戏在线词典项目 Go 语言的实战案例 | 青训营 GO语言工程实践课后作业:实现思路、代码以及路径记录 Go补充简介 在计算机编程领域,Go 语言(也称为 Golang&…

二叉树---前,中,后序遍历做题技巧(前,中,后,层次,线索二叉树)

1.由二叉树求前,中,后序遍历 前序:根左右(每一个小方块都遵循) 得到:A,B,D,H,E,I,C,F,G 中序:左根右(每一个小方块都遵循) 得到:H,D,B,I,E,A,F,C,G 后序:左右…

js 模块 简单实验

1.代码 1.1 t2.js var year "test"; export { year }; 1.2 t1.js import { year } from ./t2.jsalert(year); 1.3 t.html <script type"module" src"./t1.js"></script> 2.运行结果

MyBatis动态SQL、模糊查询与结果映射

目录 前言 一、MyBatis动态SQL 1.动态SQL是什么 2.动态SQL的作用 3.常用动态SQL元素 1. where if 元素 2. set if 元素 3. choose when otherwise 元素 4. 自定义 trim 元素 <1>. 自定义 trim 元素改写上面的 where if 语句 <2>. 自定义 trim 元素改…

Go 语言的实战案例 SOCKS5 代理 | 青训营

Powered by:NEFU AB-IN 文章目录 Go 语言的实战案例 SOCKS5 代理 | 青训营 引入TCP echo serverauth 认证请求阶段relay阶段 Go 语言的实战案例 SOCKS5 代理 | 青训营 GO语言工程实践课后作业&#xff1a;实现思路、代码以及路径记录 引入 代理是指在计算机网络中&#xff…

硬件知识积累 LED的介绍与选型 (简单电路)

1. LED 的介绍 1.1 LED 是什么 LED :是一种能发光的半导体电子元件。发光二极管&#xff08;LED&#xff09;于20世纪60年代问世。在20世纪80年代之前&#xff0c;LED主要作为指示灯使用&#xff0c;从其光色来看&#xff0c;只有红光、橙光、黄光和绿光等几种。这一时期属于…

MyBatis动态语句且如何实现模糊查询及resultType与resultMap的区别---详细介绍

前言 前面我们学习了如何使用Mybatis实现简单的增删改查。今天我们来学习如何使用动态语句来根据不同的条件生成不同的SQL语句。这在实际开发中非常有用&#xff0c;因为通常查询条件是多样化的&#xff0c;需要根据实际情况来拼接SQL语句&#xff0c;那什么是MyBatis动态语句呢…

【Docker入门第一篇】

Docker简介 Docker 是一个开源的应用容器引擎&#xff0c;基于 Go 语言 并遵从 Apache2.0 协议开源。 Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&#xff0c;也可以实现虚拟化。 容器是完全使…

mysql数据备份批处理文件正式版已测试通过

这里写目录标题 1.数据库全表原始数据.ibd文件备份为.sql文件1.1理解date含义 2.备份原始表中部分表 --改进版2.1 backPartTable表内容2.2备份运行2.3实操代码 3.如何将备份的文件进行压缩处理&#xff1f;&#xff1f;执行压缩文件 1.数据库全表原始数据.ibd文件备份为.sql文件…

Android相机-HAL-Rockchip-hal3

引言&#xff1a; 对于Android相机的 HAL层而言对上实现一套Framework的API接口&#xff0c;对下通过V4L2框架实现与kernel的交互。不同的平台会有不同的实现方案。主要是对Android HAL3的接口的实现。看看rockchip是怎么支持hal3的&#xff1f; 代码目录&#xff1a; hardw…

浅析Linux追踪技术之kprobe:基于kprobes的Event Tracing

文章目录 概述内核选项配置ftrace配置接口kprobe事件配置 使用示例添加kprobes事件kprobes事件使能kprobes事件统计 参考链接 概述 常规的Event Tracing&#xff08;事件追踪&#xff09;是通过散落在Linux内核代码各处的Tracepoint来实现的&#xff0c;这些Tracepoint数量有限…

Docker部署MongoDB 5.0.5

1、查看目录 rootwielun:~# tree mongo mongo ├── conf │ └── mongod.conf ├── data ├── docker-compose.yml └── logrootwielun:~# cd mongo rootwielun:~/mongo# chmod 777 log2、配置docker-compose.yml rootwielun:~/mongo# cat docker-compose.yml ve…

RK3568 uart串口

一.简介 串口全称叫做串行接口&#xff0c;通常也叫做 COM 接口&#xff0c;串行接口指的是数据一个一个的顺序传 输&#xff0c;通信线路简单。使用两条线即可实现双向通信&#xff0c;一条用于发送&#xff0c;一条用于接收。串口通信 距离远&#xff0c;但是速度相对会低&a…

YOLOv5算法改进(3)— 添加CBAM注意力机制

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。注意力机制是近年来深度学习领域内的研究热点&#xff0c;可以帮助模型更好地关注重要的特征&#xff0c;从而提高模型的性能。CBAM&#xff08;Convolutional Block Attention Module&#xff09; 是一种用于前馈卷积神经…

Node基础--Node简介以及安装教程

1.Node简介 Node.js发布于2009年5月&#xff0c;由Ryan Dahl开发&#xff0c;是一个基于Chrome V8引擎的JavaScript运行环境&#xff0c;使用了一个事件驱动、非阻塞式I/O模型&#xff0c;让JavaScript 运行在服务端的开发平台&#xff0c;它让JavaScript成为与PHP、Python、Pe…

使用Maven父工程构建spring boot子工程

1.父工程删除src目录&#xff0c;pom文件配置parent为spring-boot-starter-parent 2.创建子工程&#xff0c;子工程引入一个springboot相关依赖 注意&#xff1a;子工程引入springboot相关依赖之后子工程才能被解析为springboot模块

Android SDK 上手指南||第五章 用户界面设计

第五章 用户界面设计 在本篇教程中我们将为应用程序项目添加布局方案&#xff0c;在这方面XML与Eclipse ADT接口将成为工作中的得力助手——不过在后面两节中还会用到一部分Java开发知识。XML与Java在Android平台的开发工作当中可谓无处不在&#xff0c;如果大家对二者还缺乏基…

Windows运行Spark所需的Hadoop安装

解压文件 复制bin目录 找到winutils-master文件hadoop对应的bin目录版本 全部复制替换掉hadoop的bin目录文件 复制hadoop.dll文件 将bin目录下的hadoop.dll文件复制到System32目录下 配置环境变量 修改hadoop-env.cmd配置文件 注意jdk装在非C盘则完全没问题&#xff0c;如果装在…