当前位置: 首页 > news >正文

FreeRTOS学习笔记(七)信号量

文章目录

  • 前言
  • 一、信号量的基础知识
    • 1.1 信号量与队列的关系
    • 1.2 优先级反转
  • 二、FreeRTOS信号量
    • 2.1 二值信号量(Binary Semaphore)
    • 2.2 计数信号量(Counting Semaphore)
    • 2.3 互斥信号量(Mutex)


前言

  信号量,我们在之前网络编程的时候就讲过它的详细概念了,感兴趣的朋友可以回顾一下,这里介绍一下其在Freertos中的用法。

一、信号量的基础知识

1.1 信号量与队列的关系

  信号量主要的功能就是实现任务之间的同步与互斥,实现的方式主要就是依靠队列(信号量是特殊的队列)的任务阻塞机制。具体来说,信号量就是大小为1的队列,信号量的“获取”和“释放”操作对应着队列的“发送”和“接收”。与此同时这样,我们用于队列的很多函数,也就成信号量的底层函数,例如:xSemaphoreTake()实际上是 xQueueReceive() 的封装;xSemaphoreGive() 是 xQueueSend() 的封装;对于在中断中使用的信号量操作,如xSemaphoreGiveFromISR(),对应的是 xQueueSendFromISR()。
  那么为什么我们以及学习了队列还要学习信号量呢?其实还是为了“省”,前面介绍过队列常用来进行数据的传递,但是我们在同步或者互斥的之后却只需要确定队列中还有没有成员。如此一来我们就可以利用信号量来简化代码结构,而无需为信号量的实现单独构建一个冗长的队列。

1.2 优先级反转

  优先级反转指的是一个高优先级的任务由于等待某个共享资源,而被迫让位于低优先级的任务,进而导致系统中的中等优先级任务抢占 CPU 资源。这种情况下,系统中的优先级关系被打破,高优先级任务的执行反而延后了。
  例如如果任务B(低优先级)持有一个共享资源,任务A(高优先级)试图获取该资源,但任务A无法获取而被阻塞。此时任务C(中优先级)由于优先级高于任务B而获得了CPU的控制权,继续执行,导致任务A无法继续执行,尽管它的优先级高于任务C。最终任务A的执行被任务C延迟,而这个过程违背了优先级调度的原则。
  优先级反转会带来系统调度的混乱,尤其在实时系统中,高优先级任务可能是一个时间敏感的任务。如果优先级反转持续存在,它可能导致高优先级任务不能在规定时间内完成,甚至引发系统的故障或崩溃。
  为了解决优先级反转问题,操作系统通常引入优先级继承机制,即当一个低优先级任务持有某个共享资源时,如果一个高优先级任务试图访问这个资源并被阻塞,操作系统会将低优先级任务的优先级“继承”为高优先级任务的优先级,直到它释放该资源。这意味着低优先级任务会立即完成其对资源的操作,进而释放资源,使得高优先级任务可以继续执行。

二、FreeRTOS信号量

  在FreeRTOS中,信号量(Semaphore)是一种用于管理任务间同步和共享资源的机制,类似于Linux中的信号量机制。它可以用于控制对共享资源(如硬件外设、内存等)的访问,以及任务之间的同步。FreeRTOS的信号量可以分为几种主要类型,每种类型都有不同的应用场景:

  • 二元信号量(Binary Semaphore):实现为长度为 1、项大小为 0 的队列。
  • 计数信号量(Counting Semaphore):实现为长度为 N、项大小为 0 的队列,其中 N 是信号量的最大计数值。
  • 互斥量(Mutex):也是一种特殊的二元信号量,具有优先级继承等特性。

2.1 二值信号量(Binary Semaphore)

  二值信号量的行为类似于互斥锁或开关,它其实可以理解为一个只有一个队列项的队列,这个特殊的队列要么是满的,要么是空。它通常用于同步任务,或者保护对共享资源的独占访问。和队列一样,信号量 API 函数允许设置一个阻塞时间,阻塞时间是当任务获取信号量的时候由于信号量无效从而导致任务进入阻塞态的最大时钟节拍数。如果多个任务同时阻塞在同一一个信号量上的话那么优先级最高的哪个任务优先获得信号量,这样当信号量有效的时候高优先级的任务就会解除阻塞状态。
xSemaphoreCreateBinary()
  此函数是 vSemaphoreCreateBinary()的新版本,新版本的 FreeRTOS 中统一用此函数来创建二值信号量。使用此函数创建二值信号量的话信号量所需要的 RAM 是由 FreeRTOS 的内存管理部分来动态分配的。此函数创建好的二值信号量默认是空的,也就是说刚创建好的二值信号量使用函数 xSemaphoreTake()是获取不到的,此函数也是个宏,具体创建过程是由函数xQueueGenericCreate()来完成的,函数原型如下:

SemaphoreHandle_t xSemaphoreCreateBinary( void )
  • 参数:无
  • 返回值:
    • 成功: 创建成功的二值信号量的句柄
    • 失败: NULL

xSemaphoreGive()xSemaphoreGiveFromISR()
  xSemaphoreGive()用于释放二值信号量、计数型信号量或互斥信号量,此函数是一个宏,真正释放信号量的过程是由函数 xQueueGenericSend()来完成的,函数原型如下:

BaseType_t xSemaphoreGive( xSemaphore )
  • 参数:
    • xSemaphore:要释放的信号量句柄。
  • 返回值:
    • 成功:pdPASS
    • 失败:errQUEUE_FULL

   xSemaphoreGiveFromISR()函数用于在中断中释放信号量,此函数只能用来释放二值信号量和计数型信号量,绝对不能用来在中断服务函数中释放互斥信号量。

BaseType_t xSemaphoreGiveFromISR( SemaphoreHandle_t xSemaphore,BaseType_t * pxHigherPriorityTaskWoken)
  • 参数:
    • xSemaphore: 要释放的信号量句柄
    • pxHigherPriorityTaskWoken: 标记退出此函数以后是否进行任务切换

:pxHigherPriorityTaskWoken变量的值不用手动进行设置,用户只需要提供一个变量来保存这个值就行了,当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。

  • 返回值:
    • 成功:pdPASS
    • 失败:errQUEUE_FULL

xSemaphoreTake()xSemaphoreTakeFromISR()
  xSemaphoreTake()函数用于获取二值信号量、计数型信号量或互斥信号量,函数原型如下:

BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,TickType_t xBlockTime)
  • 参数:
    • xSemaphore:要获取的信号量句柄
    • xBlockTime: 阻塞时间
  • 返回值:
    • 成功:pdTRUE
    • 失败:pdFALSE

  xQueueReceiveFromISR ()函数用于在中断服务函数中获取信号量,此函数用于获取二值信号量和计数型信号量,此函数原型如下:

BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore,BaseType_t * pxHigherPriorityTaskWoken)
  • 参数:
    • xSemaphore: 要获取的信号量句柄
    • pxHigherPriorityTaskWoken: 标记退出此函数以后是否进行任务切换
  • 返回值:
    • 成功:pdTRUE
    • 失败:pdFALSE

2.2 计数信号量(Counting Semaphore)

  计数信号量允许多个任务或中断同时释放信号量,并且允许多个任务等待信号量。它的作用类似于一个计数器,可以在多个任务或中断中同时处理多个事件。它限制访问某个共享资源的并发任务数,也可以计数某个事件的发生次数,比如传感器数据的更新次数。
xSemaphoreCreateCounting()
  此函数用于创建一个计数型信号量,所需要的内存通过动态内存管理方法分配。此函数原型如下:

SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, 
UBaseType_t uxInitialCount )
  • 参数:
    • uxMaxCount: 计数信号量最大计数值,当信号量值等于此值的时候释放信号量就会失败。
    • uxInitialCount: 计数信号量初始值。
  • 返回值:
    • 成功:返回计数型信号量句柄
    • 失败:NULL

:互斥信号量、计数型信号量的释放和获取与二值信号量相同。

2.3 互斥信号量(Mutex)

  互斥信号量用于实现任务间的互斥访问,确保只有一个任务能够访问某一共享资源。与二值信号量不同,互斥信号量可以防止优先级反转(priority inversion),即较低优先级任务占用资源时,高优先级任务被阻塞的问题。
xSemaphoreCreateMutex()
  此函数用于创建一个互斥信号量,所需要的内存通过动态内存管理方法分配。此函数原型如下:

SemaphoreHandle_t xSemaphoreCreateMutex( void )
  • 参数:无。
  • 返回值:
    • 成功: 创建成功的互斥信号量的句柄
    • 失败:NULL: 互斥信号量创建失败

免责声明:本文参考了网上公开资料,仅用于学习交流,若有错误或侵权请联系笔者。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 《C++代码高度优化之双刃剑:避免过度优化引发的“暗雷”》
  • MySQL中的redo log、 undo log、bin log
  • flink中startNewChain() 的详解
  • 【计网】从零开始使用UDP进行socket编程 --- 服务端业务实现
  • 相亲交友中的用户画像构建方法探讨
  • cfs三层靶机——内网渗透
  • centos中yum方式部署Jenkins
  • git github仓库管理
  • idea激活页面怎么打开
  • 搜索二叉树BSTree的原理及实现
  • 监控系列之-prometheus部署说明
  • 服务器搭建FTP服务
  • SurfaceTexture OnFrameAvailableListener 调用流程分析
  • C++11的部分新特性
  • 《微信小程序实战(1)· 开篇示例 》
  • C++回声服务器_9-epoll边缘触发模式版本服务器
  • iOS帅气加载动画、通知视图、红包助手、引导页、导航栏、朋友圈、小游戏等效果源码...
  • MYSQL如何对数据进行自动化升级--以如果某数据表存在并且某字段不存在时则执行更新操作为例...
  • vue:响应原理
  • 欢迎参加第二届中国游戏开发者大会
  • 每天一个设计模式之命令模式
  • 微服务入门【系列视频课程】
  • 为物联网而生:高性能时间序列数据库HiTSDB商业化首发!
  • 曜石科技宣布获得千万级天使轮投资,全方面布局电竞产业链 ...
  • ​Linux·i2c驱动架构​
  • ​中南建设2022年半年报“韧”字当头,经营性现金流持续为正​
  • # 利刃出鞘_Tomcat 核心原理解析(八)-- Tomcat 集群
  • #Datawhale AI夏令营第4期#多模态大模型复盘
  • #QT(TCP网络编程-服务端)
  • %@ page import=%的用法
  • (Mirage系列之二)VMware Horizon Mirage的经典用户用例及真实案例分析
  • (Redis使用系列) Springboot 使用redis的List数据结构实现简单的排队功能场景 九
  • (补充):java各种进制、原码、反码、补码和文本、图像、音频在计算机中的存储方式
  • (二)windows配置JDK环境
  • (二刷)代码随想录第15天|层序遍历 226.翻转二叉树 101.对称二叉树2
  • (附源码)c#+winform实现远程开机(广域网可用)
  • (三)centos7案例实战—vmware虚拟机硬盘挂载与卸载
  • (太强大了) - Linux 性能监控、测试、优化工具
  • (转)大型网站的系统架构
  • **登录+JWT+异常处理+拦截器+ThreadLocal-开发思想与代码实现**
  • .htaccess配置常用技巧
  • .NET Core WebAPI中使用swagger版本控制,添加注释
  • .NET 给NuGet包添加Readme
  • .net 中viewstate的原理和使用
  • .Net6使用WebSocket与前端进行通信
  • .NET中两种OCR方式对比
  • ??myeclipse+tomcat
  • @html.ActionLink的几种参数格式
  • [@Controller]4 详解@ModelAttribute
  • [2024-06]-[大模型]-[Ollama] 0-相关命令
  • [Android Pro] android 混淆文件project.properties和proguard-project.txt
  • [BZOJ 3680]吊打XXX(模拟退火)
  • [C/C++] C/C++中数字与字符串之间的转换
  • [CTF]php is_numeric绕过
  • [Electron]ipcMain.on和ipcMain.handle的区别