【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();

  // 创建任务 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.mfbz.cn/a/228709.html

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

相关文章

MacBook 逆水寒下载安装使用教程,支持最新版本 MacOS 流畅不闪退

最近 MacBook 系统更新到了 MacOS 14.1 很多朋友的逆水寒玩不了了,我尝试了一番可以正常玩了,看图: 其实操作也很简单,我们从头开始,因为 MacOS 系统的更新所以我们也需要更新新版本的 playCover 来适配新的系统&#…

SpringSecurity(二)

SpringSecurity源码的初探 一、SpringSecurity中的核心组件 在SpringSecurity中的jar分为4个,作用分别为 jar作用spring-security-coreSpringSecurity的核心jar包,认证和授权的核心代码都在这里面spring-security-config如果使用Spring Security XML名称…

速查!软考出成绩了

2023年11月软考成绩出来啦!大家赶紧查一下,各科都45分就是通过! 01 如何查成绩 1、打开“中国计算机技术职业资格网”,网址:https://www.ruankao.org.cn/ 2、点击↘的“成绩查询”按钮。 3、输入“手机号/证件号密码验…

C //例10.3 从键盘读入若干个字符串,对它们按字母大小的顺序排序,然后把排好序的字符串送到磁盘文件中保存。

C程序设计 (第四版) 谭浩强 例10.3 例10.3 从键盘读入若干个字符串,对它们按字母大小的顺序排序,然后把排好序的字符串送到磁盘文件中保存。 IDE工具:VS2010 Note: 使用不同的IDE工具可能有部分差异。 代码块 方法…

搭乘“低代码”快车,引领食品行业数字化转型全新升级

数字化技术作为重塑传统行业重要的力量,正以不可逆转的趋势改变着企业经营与客户消费的方式。 在近些年的企业数字化服务与交流过程中,织信团队切实感受到大多数企业经营者们从怀疑到犹豫再到焦虑最终转为坚定的态度转变。 在这场数字化转型的竞赛中&a…

[wp]“古剑山”第一届全国大学生网络攻防大赛 Web部分wp

“古剑山”第一届全国大学生网络攻防大赛 群友说是原题杯 哈哈哈哈 我也不懂 我比赛打的少 Web Web | unse 源码&#xff1a; <?phpinclude("./test.php");if(isset($_GET[fun])){if(justafun($_GET[fun])){include($_GET[fun]);}}else{unserialize($_GET[…

口袋参谋:如何写出爆款标题,流量翻倍?

​“王老师&#xff0c;我店铺的宝贝转化好低呀&#xff01;都没人点&#xff01;这是什么原因&#xff1f;”这不上周刚有位商家来咨询我&#xff0c;今天咱们就这个问题好好谈谈&#xff0c;为什么宝贝点击那么差&#xff1f; 首先我们要搞清楚一个逻辑&#xff0c;那就是要先…

XXL-JOB日志相关报错的原因

1.问题&#xff1a;msg&#xff1a;job handler [myJobHandler] not found. 原因&#xff1a;执行器中没有对应的执行器。 执行器中代码展示&#xff1a; Component Slf4j public class JobHandler {XxlJob(value "abcHandler")public void abcHandler() {log.inf…

VUE3给table的head添加popover筛选、时间去除时分秒、字符串替换某字符

1. VUE3给table的head添加popover筛选 <el-tableref"processTableRef"class"process-table"row-key"secuId":data"pagingData"style"width: 100%"highlight-current-row:height"stockListHeight":default-exp…

虚幻学习笔记10—C++函数与蓝图的通信

一、前言 除了上一章C变量与蓝图通信讲的变量能与蓝图通信外&#xff0c;还有函数和枚举也可以和蓝图通信。函数的关键字为”UFUNCTION“、枚举的关键字为”UENUM“。 二、实现 2.1、BlueprintCallable蓝图中调用 该函数时带执行的&#xff0c;带入如下。编译成功后在蓝图中输…

node后端接口无法插入数据为emoji的表情的问题

原因 emoji的表情一般是这样的\xF0\x9F\x98\x80或者是\xF0\x9F\x98 &#xff0c;事实上 一般数据库的utf8的编码类型都是能保存\xF0\x9F\x98 但是不能保存\xF0\x9F\x98\x80这种样的emoji&#xff0c;要将数据库编码格式为utf8mb4 也就是utf8的超集 另外&#xff0c;除了 数据库…

我的创作纪念日——多线程进阶分享

多线程-进阶 1. 锁的策略 1.1 乐观锁&悲观锁 乐观锁 预计在线程中数据大概率不会被其他线程拿去修改 对于加锁所作的准备较少。只有当修改的操作真正发生了&#xff0c;才会进行加锁操作 所以乐观锁适用于多读少写的情况&#xff0c;可以降低加锁频率&#xff0c;提升效…

车载测试:如何用CANape进行ADAS实车功能测试?

前言 CANape是一款用于ECU测量、标定、诊断以及ADAS传感器数据采集的工具型软件。 测量——通过CANape不仅能采集记录ECU内部信号&#xff0c;还支持与车辆上的各种传感器的总线进行通信。与ECU不同&#xff0c;ADAS传感器不提供车辆实际运行信号&#xff0c;而是提供车辆运行…

3亿人民币!欧洲高性能计算联合企业从德国开始建设量子生态

&#xff08;图片来源&#xff1a;网络&#xff09; 欧洲高性能计算联合企业&#xff08;EuroHPC JU&#xff09;已启动招标程序&#xff0c;准备在德国部署一台新型Euro-Q-Exa量子计算机。 Euro-Q-Exa系统将是一台基于超导量子比特和最先进的技术研发的量子计算机&#xff0…

细粒度视觉分类的注意内核编码网络

Attentional Kernel Encoding Networks for Fine-Grained Visual Categorization 1、介绍2、方法2.1 卷积模块2.2 级联注意力模块2.3 内核编码模块2.4 整体 3、结论 在本文中&#xff0c;我们提出了一种用于细粒度视觉分类的注意核编码网络(AKEN)。具体来说&#xff0c;AKEN聚合…

RK3588+MCU机器人控制器解决方案

1 产品简介 XMP04A 是一款信迈科技基于 RK3588 设计的高性能、低功耗的边缘计算设备&#xff0c; 内置 NPU 算力可达 6.0TOPSINT8&#xff0c;以及具备强大的视频编解码能力&#xff0c;最高可支持 32 路 1080P30fps 解码和 16 路 1080P30fps 编码 &#xff0c;支持 4K12…

浙政钉SDK安装

专有订单SDK&#xff08;jar包&#xff09;下载 专有钉钉门户 (dg-work.cn) Maven依赖 浙政钉 <!-- 浙政钉 --> <dependency><groupId>com.oracel</groupId><artifactId>zwdd-sdk-java</artifactId><version>1.2.0</version…

【推荐系统】推荐算法数学基础

【大家好&#xff0c;我是爱干饭的猿&#xff0c;本文重点介绍推荐系统涉及的数学知识、推荐系统涉及的概率统计知识。 后续会继续分享其他重要知识点总结&#xff0c;如果喜欢这篇文章&#xff0c;点个赞&#x1f44d;&#xff0c;关注一下吧】 上一篇文章&#xff1a;《【推…

2008. 出租车的最大盈利(动态规划)

按照 end 对每一个乘客进行排序对于每一个乘客&#xff0c;如果接他就要寻找之前小于 start 的 end 中最大的那个来计算接他的收益&#xff0c;而不接的话就是上一个乘客的收益&#xff0c;取最大值即可 class Solution:def maxTaxiEarnings(self, n: int, rides: List[List[i…

flask web开发学习之初识flask(三)

文章目录 一、flask扩展二、项目配置1. 直接配置2. 使用配置文件3. 使用环境变量4. 实例文件夹 三、flask命令四、模版和静态文件五、flask和mvc架构 一、flask扩展 flask扩展是指那些为Flask框架提供额外功能和特性的库。这些扩展通常遵循Flask的设计原则&#xff0c;易于集成…
最新文章