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

在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();// 创建任务 Task1xTaskCreate(Task1, "Task1", configMINIMAL_STACK_SIZE, NULL, 1, NULL);// 创建任务 Task2xTaskCreate(Task2, "Task2", configMINIMAL_STACK_SIZE, NULL, 2, NULL);// 启动调度器vTaskStartScheduler();while (1) {// 如果调度器启动失败,进行错误处理// ...}
}

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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Kubeadm构建K8S集群指南:从环境准备到Dashboard部署的详细步骤与常见问题解决方案

文章目录 一、环境准备1、准备1主2从2、设置主机名与时区3、添加hosts网络主机配置4、关闭防火墙5、验证是否配置正确 二、安装Kubeadm1、在每个Centos上安装Docker2、确保从cgroups均在同一个从groupfs3、安装kubeadm集群部署工具4、关闭交换区5、配置网桥6、通过镜像安装k8s7…

SIT3232E高静电防护,单电源供电,双通道,RS232 收发器

SIT3232E 是一款 3.0V~5.5V 供电、双通道、低功耗、高静电防护 ESD 保护,完全满足 TIA/EIA-232 标准要求的 RS-232 收发器。 SIT3232E 包括两个驱动器和两个接收器,具有增强形 ESD 保护功能,达到 15kV 以上 HBM ESD 、 8kV …

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中…

软考2018下午第六题改编逻辑(状态模式)

在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象 package org.example.状态模式.软考航空;/*** author lst* date 2023年12月07日 15:37*/ class FrequentFlyer {CState state;double flyMiles;public FrequentFlyer() {…

IDEA 出现问题:Idea-操作多次commit,如何合并为一个并push解决方案

❤️作者主页:小虚竹 ❤️作者简介:大家好,我是小虚竹。2022年度博客之星评选TOP 10🏆,Java领域优质创作者🏆,CSDN博客专家🏆,华为云享专家🏆,掘金年度人气作…

HHDESK右键管理简介

在HHDESK管理文件,除了基本的打开、删除、复制、粘贴、重命名外,还有多种便捷编辑方式。 可以分别以下列模式打开文档: 文本模式即是以文本编辑器打开文档。 1 二进制模式 可进行二进制编辑。 2 JSON模式 可对JSON文件进行直观的解析…

本地团购分销:解密最新赚钱模式,带你开启财富之门!APP小程序H5三端源码交付,支持二开!

近年来,随着互联网的迅猛发展和电子商务的普及,本地团购分销逐渐成为了一种热门的赚钱模式。它不仅为消费者提供了更多的购物优惠,同时也给商家和分销商带来了可观的利润。在这篇文章中,我们将详细介绍本地团购分销的运作原理和盈…

【操作系统笔记】-文件系统

引言 之前已经学习过数据在内存中是如何表示,如何存储,但是这些存储在PC断电后数据便消失。因此我们需要一个可以持久化存储并且容量远远大于内存的结构,这一篇我们将学习,文件是如何被组织和操作的,这是一个操作系统…

IO流(Java)

IO流 在学习IO流之前,我们首先了解一下File File File即文件或文件夹路径对象,其示例类可以是存在路径也可以是未创造路径 File有什么用 用于创建或操作文件或文件夹 File常用API API部分看得懂会查会用即可 IO流 IO(Input 读数据 Output写数据…

AMEYA360-罗姆ROHM开发出采用SOT-223-3小型封装的600V耐压Super Junction MOSFET

全球知名半导体制造商ROHM(总部位于日本京都市)开发出采用SOT-223-3小型封装(6.50mm7.00mm1.66mm)的600V耐压Super Junction MOSFET*1“R6004END4 / R6003KND4 / R6006KND4 / R6002JND4 / R6003JND4”,新产品非常适用于照明用小型电源、电泵和电机等应用。 近年来&a…

【展望2024】,从软件测试用例开始学习起

1. 测试用例的概念 测试用例就是测试人员向被测试系统发起的一组集合,该集合包括测试环境,测试数据,测试步骤,预期结果 2. 设计测试用例的好处 在测试前都要先设计测试用例,设计测试用例有如下好处: 测…

Mysql 索引概念回顾

一、什么是索引 在关系数据库中,索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。索引的作用相当于图书的目录,可以根据…