【FreeRTOS】信号量——简介、常用API函数、注意事项、项目实现

news2024/11/18 1:44:09

在FreeRTOS中,信号量是一种非常重要的同步机制,用于实现任务间的互斥访问和同步操作。通过信号量,不同的任务可以安全地共享资源,避免竞争和冲突,从而确保系统的稳定性和可靠性。本篇博客将介绍FreeRTOS中信号量的基本概念、使用方法和实际应用,帮助读者深入理解信号量的原理和在实际项目中的应用场景。我们将从信号量的创建、获取和释放等方面进行详细讲解,并结合实际的示例代码,帮助读者更好地掌握FreeRTOS中信号量的使用技巧和注意事项。通过本篇博客的学习,读者将能够更加灵活地运用信号量来实现任务间的同步和资源共享,提高系统的可靠性和效率。


文章目录

    • 1.信号量简介
      • 1.1 二值信号量
      • 1.2 计数信号量
      • 1.3 互斥信号量
      • 1.4 递归信号量
      • 1.5 信号量控制块
    • 2.常用信号量API函数
      • 2.1 创建信号量函数
        • 2.1.1 创建二值信号量 xSemaphoreCreateBinary()
        • 2.1.2 创建计数信号量 xSemaphoreCreateCounting()
      • 2.2 信号量删除函数 vSemaphoreDelete()
      • 2.3 信号量释放函数
        • 2.3.1 xSemaphoreGive()
        • 2.3.2 xSemaphoreGiveFromISR()
      • 2.4 信号量获取函数
        • 2.4.1 xSemaphoreTake()
        • 2.4.2 xSemaphoreTakeFromISR()
    • 3.例子说明


1.信号量简介

信号量Semaphore)是一种实现任务间通信的机制,可以实现任务之间同步或临界资源的互斥访问,常用于协助一组相互竞争的任务来访问临界资源。在多任务系统中,各任务之间需要同步或互斥实现临界资源的保护,信号量功能可以为用户提供这方面的支持。

1.1 二值信号量

二值信号量既可以用于临界资源访问也可以用于同步功能。

在这里插入图片描述

只能取两个值:01。这种信号量通常用于实现互斥访问,即只有一个进程或线程可以访问共享资源。当一个进程或线程占用资源时,二值信号量的值为1,其他进程或线程需要等待,直到信号量的值变为0才能访问资源。

二值信号量通常用于解决临界区问题,即多个进程或线程需要访问共享资源时可能会导致数据不一致或竞争条件的问题。通过使用二值信号量,可以有效地控制对共享资源的访问,避免出现这些问题。

在实际应用中,二值信号量通常与互斥锁mutex)结合使用,以实现对共享资源的互斥访问。当一个进程或线程需要访问共享资源时,首先尝试对二值信号量进行加操作,如果成功则可以访问资源,否则需要等待。在访问完成后,再对二值信号量进行减操作,释放资源。


1.2 计数信号量

二进制信号量可以被认为是长度为 1 的队列,而计数信号量则可以被认为长度大于 1的队列,信号量使用者依然不必关心存储在队列中的消息,只需关心队列是否有消息即可。

在这里插入图片描述

一种可以取多个值的信号量,它用于控制对一组资源的访问数量。计数信号量的值可以大于等于0,表示可用的资源数量。当一个进程或线程需要访问资源时,它会尝试对计数信号量进行减操作,如果计数信号量的值大于0,则可以访问资源,同时计数信号量的值会减少;如果计数信号量的值为0,则需要等待,直到有其他进程或线程释放资源,使计数信号量的值大于0。

计数信号量通常用于实现对一组资源的并发访问控制,例如限制同时访问某个资源的进程或线程的数量。这种机制可以有效地控制资源的并发访问,避免资源被过度占用,提高系统的性能和稳定性。

在实际应用中,计数信号量可以用于实现线程池、连接池等并发控制的场景,以及限制对其他有限资源的并发访问。通过合理地管理计数信号量的值,可以有效地控制对资源的并发访问,避免出现竞争条件和数据不一致的问题。


1.3 互斥信号量

互斥信号量其实是特殊的二值信号量,由于其特有的优先级继承机制从而使它更适用于简单互锁,也就是保护临界资源。

在这里插入图片描述

只能取两个值:0和1。它用于实现对共享资源的互斥访问,即只有一个进程或线程可以访问共享资源。当一个进程或线程占用资源时,互斥信号量的值为1,其他进程或线程需要等待,直到信号量的值变为0才能访问资源。

互斥信号量通常与互斥锁(mutex)等同步机制结合使用,以实现对共享资源的互斥访问。在访问共享资源之前,进程或线程会尝试对互斥信号量进行减操作,如果成功则可以访问资源,否则需要等待。在访问完成后,再对互斥信号量进行加操作,释放资源。

互斥信号量是实现进程同步和互斥的重要工具,它能够有效地避免竞争条件和数据不一致的问题。在并发编程中,互斥信号量通常用于保护临界区,即一段代码在同一时间只能被一个线程执行,以确保数据的一致性和正确性。


1.4 递归信号量

允许同一线程多次对信号量进行加操作,而不会导致死锁。通常情况下,普通的信号量在同一线程内多次对信号量进行加操作可能会导致死锁,因为信号量的值会被多次减少,但只有一次加操作来释放资源。

递归信号量则允许同一线程多次对信号量进行加操作,每次加操作都会增加信号量的值,从而避免了死锁的情况。这种机制通常用于需要在递归函数中对共享资源进行加锁的情况,以及其他需要在同一线程内多次对资源进行加锁的情况。


1.5 信号量控制块

在这里插入图片描述
在这里插入图片描述


2.常用信号量API函数

2.1 创建信号量函数

2.1.1 创建二值信号量 xSemaphoreCreateBinary()
  • 原型:SemaphoreHandle_t xSemaphoreCreateBinary( void );
  • 作用:用于创建一个二值信号量,用于实现对共享资源的互斥访问。
  • 参数:无参数。
  • 返回值:返回一个 SemaphoreHandle_t 类型的句柄,表示创建的二值信号量。

xSemaphoreCreateBinary() 函数的作用是创建一个二值信号量,二值信号量的初始值为0,用于实现对共享资源的互斥访问。它返回一个 SemaphoreHandle_t 类型的句柄,可以用于后续对该二值信号量进行操作。


2.1.2 创建计数信号量 xSemaphoreCreateCounting()
  • 原型:SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount );
  • 作用:用于创建一个计数信号量,用于控制对一组资源的并发访问数量。
  • 参数:
    • uxMaxCount:计数信号量的最大计数值。
    • uxInitialCount:计数信号量的初始计数值。
  • 返回值:返回一个 SemaphoreHandle_t 类型的句柄,表示创建的计数信号量。

xSemaphoreCreateCounting() 函数用于创建一个计数信号量,计数信号量的初始值为 uxInitialCount,最大计数值为 uxMaxCount。计数信号量通常用于限制对一组资源的并发访问数量,通过合理地管理计数信号量的值,可以有效地控制对资源的并发访问,避免出现竞争条件和数据不一致的问题。


2.2 信号量删除函数 vSemaphoreDelete()

  • 原型:void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );
  • 作用:用于删除一个已经创建的信号量。
  • 参数:
    • xSemaphore:要删除的信号量的句柄。
  • 返回值:无。

vSemaphoreDelete() 函数用于删除一个已经创建的信号量,通过传入要删除的信号量的句柄 xSemaphore 来实现。一旦删除了信号量,其句柄将不再有效,并且不能再对该信号量进行操作。


2.3 信号量释放函数

2.3.1 xSemaphoreGive()
  • 原型:BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
  • 作用:用于释放一个二值信号量或增加一个计数信号量的计数值。
  • 参数:
    • xSemaphore:要释放的信号量的句柄。
  • 返回值:如果释放成功,则返回 pdTRUE,否则返回 pdFALSE。

xSemaphoreGive() 函数用于释放一个二值信号量或增加一个计数信号量的计数值。对于二值信号量,调用该函数会将信号量的计数值从 0 增加到 1;对于计数信号量,调用该函数会增加信号量的计数值。如果有任务因等待该信号量而被唤醒,则调用 xSemaphoreGive() 会使其中一个等待的任务得以继续执行。


2.3.2 xSemaphoreGiveFromISR()
  • 原型:BaseType_t xSemaphoreGiveFromISR( SemaphoreHandle_t xSemaphore, BaseType_t *pxHigherPriorityTaskWoken );
  • 作用:用于从中断服务程序中释放一个二值信号量或增加一个计数信号量的计数值。
  • 参数:
    • xSemaphore:要释放的信号量的句柄。
    • pxHigherPriorityTaskWoken:一个指向 BaseType_t 类型的指针,用于指示是否有一个高优先级任务被唤醒。
  • 返回值:如果释放成功,则返回 pdTRUE,否则返回 pdFALSE。

xSemaphoreGiveFromISR() 函数与 xSemaphoreGive() 函数类似,用于从中断服务程序中释放信号量。它可以用于在中断服务程序中释放信号量,以唤醒等待该信号量的任务。与 xSemaphoreGive() 不同的是,xSemaphoreGiveFromISR() 可以唤醒一个等待该信号量的高优先级任务,并通过 pxHigherPriorityTaskWoken 参数指示是否有高优先级任务被唤醒。


2.4 信号量获取函数

2.4.1 xSemaphoreTake()
  • 原型:BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait );
  • 作用:用于获取一个二值信号量或减少一个计数信号量的计数值。
  • 参数:
    • xSemaphore:要获取的信号量的句柄。
    • xTicksToWait:等待获取信号量的最长时间,以时钟节拍数(tick)表示。
  • 返回值:如果成功获取信号量,则返回 pdTRUE,否则返回 pdFALSE。

xSemaphoreTake() 函数用于获取一个二值信号量或减少一个计数信号量的计数值。如果信号量的计数值大于 0,则获取成功,计数值减一;如果信号量的计数值为 0,则任务将进入阻塞状态,直到信号量可用或超时。

xTicksToWait 参数用于指定任务在获取信号量时的最长等待时间。如果设置为 portMAX_DELAY,任务将一直等待直到信号量可用;如果设置为 0,则任务将立即返回获取结果而不进行等待;如果设置为其他数值,则任务将等待指定的时钟节拍数后返回获取结果。


2.4.2 xSemaphoreTakeFromISR()
  • 原型:BaseType_t xSemaphoreTakeFromISR( SemaphoreHandle_t xSemaphore, BaseType_t *pxHigherPriorityTaskWoken );
  • 作用:用于从中断服务程序中获取一个二值信号量或减少一个计数信号量的计数值。
  • 参数:
    • xSemaphore:要获取的信号量的句柄。
    • pxHigherPriorityTaskWoken:一个指向 BaseType_t 类型的指针,用于指示是否有一个高优先级任务被唤醒。
  • 返回值:如果成功获取信号量,则返回 pdTRUE,否则返回 pdFALSE。

xSemaphoreTakeFromISR() 函数与 xSemaphoreTake() 函数类似,用于从中断服务程序中获取信号量。它可以用于在中断服务程序中获取信号量,以等待信号量可用或减少信号量的计数值。与 xSemaphoreTake() 不同的是,xSemaphoreTakeFromISR() 可以唤醒一个等待该信号量的高优先级任务,并通过 pxHigherPriorityTaskWoken 参数指示是否有高优先级任务被唤醒。


3.例子说明

当在STM32上使用FreeRTOS时,可以使用信号量来实现任务间的同步和互斥访问。以下是一个简单的示例,演示了如何在STM32上使用FreeRTOS中的信号量。

假设我们有两个任务:Task1 和 Task2,它们需要共享一个资源,我们可以使用信号量来进行同步。Task1 将获取资源并执行一些操作,然后释放资源;Task2 将等待资源可用,然后获取资源并执行操作。

#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

// 定义一个全局的信号量句柄
SemaphoreHandle_t xSemaphore;

void Task1(void *pvParameters) {
  while (1) {
    // 尝试获取信号量,等待最长100个时钟节拍
    if (xSemaphoreTake(xSemaphore, 100) == pdTRUE) {
      // 信号量获取成功,执行任务1的操作
      // ...

      // 释放信号量
      xSemaphoreGive(xSemaphore);
    } else {
      // 等待超时或者获取失败的处理
      // ...
    }
  }
}

void Task2(void *pvParameters) {
  while (1) {
    // 尝试获取信号量,等待最长100个时钟节拍
    if (xSemaphoreTake(xSemaphore, 100) == pdTRUE) {
      // 信号量获取成功,执行任务2的操作
      // ...

      // 释放信号量
      xSemaphoreGive(xSemaphore);
    } else {
      // 等待超时或者获取失败的处理
      // ...
    }
  }
}

int main(void) {
  // 创建一个二值信号量
  xSemaphore = xSemaphoreCreateBinary();

  // 创建任务 Task1
  xTaskCreate(Task1, "Task1", configMINIMAL_STACK_SIZE, NULL, 1, NULL);

  // 创建任务 Task2
  xTaskCreate(Task2, "Task2", configMINIMAL_STACK_SIZE, NULL, 2, NULL);

  // 启动调度器
  vTaskStartScheduler();

  while (1) {
    // 如果调度器启动失败,进行错误处理
    // ...
  }
}

在上面的示例中,我们首先创建了一个二值信号量 xSemaphore,然后在 Task1Task2 中使用 xSemaphoreTake()xSemaphoreGive() 来获取和释放信号量。

在每个任务中,我们使用 xSemaphoreTake() 函数来尝试获取信号量。如果获取成功,任务就可以执行相应的操作;如果获取失败,任务将等待一段时间(这里是100个时钟节拍)然后再次尝试获取。获取成功后,任务执行完操作后使用 xSemaphoreGive() 来释放信号量。

这样,通过信号量的获取和释放,我们可以实现 Task1 和 Task2 之间的资源共享和同步。这样的设计可以确保两个任务之间的操作不会相互干扰,从而实现了任务间的同步和互斥访问。

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

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

相关文章

JS原生实现浏览器滚动条滚动侧边栏高亮响应

目录 演示 ​编辑 需求 代码 css html script 代码解释 1、获取所有link-content 2、定义一个rectContent数组,然后循环allContents调用getClientRects()[0]获取每个link-content元素与浏览器视口的关系 3、为数组追加link-content,用于设置侧…

【EMNLP 2023】面向Stable Diffusion的自动Prompt工程算法BeautifulPrompt

近日,阿里云人工智能平台PAI与华南理工大学朱金辉教授团队合作在自然语言处理顶级会议EMNLP2023上发表了BeautifulPrompt的深度生成模型,可以从简单的图片描述中生成高质量的提示词,从而使文生图模型能够生成更美观的图像。BeautifulPrompt通…

谷歌Gemini刚发就惹质疑:测试标准有失偏颇,效果视频疑似剪辑

梦晨 克雷西 发自 凹非寺 量子位 | 公众号 QbitAI 谷歌憋了许久的大招,双子座Gemini大模型终于发布!其中一图一视频最引人注目: 一图,MMLU多任务语言理解数据集测试,Gemini Ultra不光超越GPT-4,甚至超越了…

拆解贝医生冲牙器F3,换电池

是这款伸缩式的,如下图, 用了差不多两年,终于充不了电,而且不能开机,估计是电池坏了,拆开试一下。 一、拆解 拆解难度不算大,本来以为这东西为了防水肯定要用一堆胶水,没想到只是卡…

十大最好猫主食罐头有哪些品牌?排名前五猫主食罐头品牌推荐

我发现不少人有这样的困扰!买到各种数值都很好的猫罐头后,猫咪一点都不吃。或者是猫咪吃了猫罐头之后,吃了一段时间后就软便身体不舒服。 猫罐头侠登场!养猫这么久了我就把我吃的不错的猫罐头分享一下!别纠结了&#…

❤ Mac IDEA使用并运行项目

❤ IDEA导入项目并运行 Mac IDEA使用 (1) 仓库导入 通过获取giett仓库包的url,在idea中导入项目 在gitee里获取项目的ur打开idea,点击 File->new->Project from Version Control (2) 创建数据库ry并导入数据脚本 (3)修改配…

Altair Radioss碰撞 安全与冲击 衡祖仿真

Altair Radioss是解决瞬态加载工况下非线性问题的领先的结构分析求解器。其具备高扩展性、高品质、高鲁棒性,以及诸多功能:多域求解技术、高级材料功能(复合材料)等。Radioss求解器被广泛应用于汽车、航空航天、电子/家电、包装、轨道机车、生物医疗、能…

harmony开发之Text组件的使用

TextInput、TextArea是输入框组件,通常用于响应用户的输入操作,比如评论区的输入、聊天框的输入、表格的输入等,也可以结合其它组件构建功能页面,例如登录注册页面。 图片来源黑马程序员 Text组件的使用: 文本显示组…

【C++】开源:Boost配置文件解析库PropertyTree配置使用

😏★,:.☆( ̄▽ ̄)/$:.★ 😏 这篇文章主要介绍Boost配置文件解析库PropertyTree配置使用。 无专精则不能成,无涉猎则不能通。——梁启超 欢迎来到我的博客,一起学习,共同进步。 喜欢的朋友可以关注…

js写旋转的时钟动态

目录 1、css代码 2.html代码 3.js代码 1、css代码 .box {position: relative;width: 600px;height: 600px;background: url(./images/clock.jpg) no-repeat center;}.hour,.minute,.second {position: absolute;left: 0;top: 0;width: 100%;height: 100%;}.hour {background…

RHEL8_Linux访问NFS存储及自动挂载

本章主要介绍NFS客户端的使用 创建FNS服务器并通过NFS共享一个目录在客户端上访问NFS共享的目录自动挂载的配置和使用 1.访问NFS存储 前面介绍了本地存储,本章就来介绍如何使用网络上的存储设备。NFS即网络文件系统,所实现的是 Linux 和 Linux 之间的共…

JOSEF 单稳态中间继电器 UEG/A-4H-L DC110V 导轨安装

系列型号 UEG/A-2H2D中间继电器UEG/A-4H4D中间继电器UEG/A-2D中间继电器 UEG/A-2H中间继电器UEG/A-4H中间继电器UEG/A-4D中间继电器 UEG/A-6H中间继电器UEG/A-6D中间继电器UEG/A-8H中间继电器 UEG/A-10D中间继电器UEG/A-10H中间继电器UEG/A-2DPDT中间继电器 UEG/A-4DPDT中…

注意力机制的快速学习

注意力机制的快速学习 注意力机制 将焦点聚焦在比较重要的事物上 我(查询对象Q),这张图(被查询对象V) 我看一张图,第一眼,就会判断那些东西对我而言比较重要,那些对于我不重要&…

JVM的内存结构详解「重点篇」

一、JVM虚拟机数据区 虚拟机栈 1、 线程私有 2、 每个方法被执行的时候都会创建一个栈帧用于存储局部变量表,操作栈,动态链接,方法出口等信息。每一个方法被调用的过程就对应一个栈帧在虚拟机栈中从入栈到出栈的过程。 3、栈帧: 是用来存储…

数字化升级,智慧医疗新时代——医院陪诊服务的技术创新

在信息技术飞速发展的今天,医疗服务正迎来数字化升级的新时代。本文将探讨如何通过先进技术的应用,为医院陪诊服务注入更多智慧元素,提升患者和家属的医疗体验。 1. 创新医疗预约系统 # Python代码演示医疗预约系统的简单实现 class Medic…

最新国内可用GPT4,Midjourney绘画网站+使用教程

一、前言 ChatGPT GPT4.0,Midjourney绘画,相信对大家应该不感到陌生吧?简单来说,GPT-4技术比之前的GPT-3.5相对来说更加智能,会根据用户的要求生成多种内容甚至也可以和用户进行创作交流。 然而,GPT-4对普…

ChatGPT有什么新奇的使用方式?

2023,ChatGPT几乎席卷了所有行业,并且具有不可测量的巨大潜力等着我们去挖掘。 越来越多人对ChatGPT的应用产生兴趣,知乎上“ChatGPT有什么新奇的使用方式?”这一个热门话题的兴起就是最好的证明。 写作,毫无疑问&…

【Java探索之旅】我与Java的初相识(一):Java的特性与优点及其发展史

🎥 屿小夏 : 个人主页 🔥个人专栏 : Java入门到精通 🌄 莫道桑榆晚,为霞尚满天! 文章目录 一. Java语言概述与优势1.1 Java的概述1.2 Java语言的优势 二. Java领域与发展史2.1 Java的使用领域2.…

中国开源大模型登顶HuggingFace排行榜

12月8日消息,全球最大的开源大模型社区HuggingFace日前公布了最新的开源大模型排行榜,阿里云通义千问力压Llama2等国内外开源大模型登顶榜首。 HuggingFace的开源大模型排行榜(Open LLM Leaderboard)是目前大模型领域最具权威性的…

基于python+unittest实现接口自动化测试

简介 本文通过从Postman获取基本的接口测试Code简单的接口测试入手,一步步调整优化接口调用,以及增加基本的结果判断,讲解Python自带的Unittest框架调用,期望各位可以通过本文对接口自动化测试有一个大致的了解。 为什么要做接口…