【ESP-IDF FreeRTOS】信号量

2024-08-31 12:20
文章标签 信号量 idf freertos esp

本文主要是介绍【ESP-IDF FreeRTOS】信号量,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

下一个内容,信号量。

先包含头文件。

#include "freertos/semphr.h"

我们通过队列可以进行任务间的数据传递,也可以通过队列来控制任务间的同步。如果我只需要控制任务而不需要传递数据,那么我们完全可以用信号量来代替队列。

简单介绍一下信号量,它约等于是没有容量的队列,或者把它当成是一个计数器。我们对信号量的操作有加一和减一。

如果信号量当前的值为0并且我需要进行减一操作,那么会阻塞,直到其他任务对信号量进行加一操作。由此我们可以发现信号量和队列的共性,信号量的加一操作就类似于队列的塞数据,信号量的减一操作就类似于队列的取数据,区别在于信号量没存数据,我们只是用它控制任务的同步。

可能有小伙伴会好奇,我直接整个全局变量去控制不就好了嘛,为什么非得用信号量。

全局变量来当信号量看起来好像没什么问题,但是确实是有问题的。

我们使用全局变量++或是全局变量--的时候,我们敲的代码是一行的,但是我们的代码终究是要转成汇编代码然后再转成机器码的,转成汇编代码的时候,我们原本一行代码的全局变量++就会变成三行,如果我这三行代码执行到一半的时候,任务切换了,导致我们未能成功将全局变量++,往小了说影响我们功能的实现,往大了说可能直接导致程序报错。

而我们的信号量属于原子操作,原子操作顾名思义就类似于原子,不能再分割了(也许吧,这属于物理范畴我不了解),也就是说当我们进行原子操作的时候,一定会执行完再切换任务,我们对信号量的操作要么一点不动,要么一定完成。

信号量我们先介绍两种,一种是计数信号量,一种是二进制信号量。差别就在于计数信号量的上限我们可以指定(最多计到多少数),而二进制信号量的上限就是1,因此二进制信号量只有两种状态,一个是空另一个是满。

我们使用下面这个宏去创建计数信号量。

xSemaphoreCreateCounting(uxMaxCount, uxInitialCount)

传入的是信号量最大值和初始值。

我们再细看一下这个宏可以发现,本质上创建信号量和创建队列是一样的。

所以理论上我们拿一个队列句柄类型的变量去接收创建得到的信号量也是可以的,并且队列集中也是可以存放信号量的,因为它们根本就是一个东西嘛。

但我们最好还是用SemaphoreHandle_t这个来接收信号量,这样见名知意。

删除信号量使用下面这个宏。

vSemaphoreDelete(xSemaphore)

宏里调用的也是删除队列的函数。

那剩下就是对信号量进行加一或减一操作了,或者换个大家比较熟悉的说法——PV操作。

先是加一操作,这里有个更形象的名字,give给也就是加一。

xSemaphoreGive(xSemaphore)

接下来是减一操作。名字也很形象,take拿也就是减一。

xSemaphoreTake(xSemaphore, xBlockTime)

上面无论是take还是give都有中断版本,也就是在函数后面加上FromISR,这边就不再介绍了。

另外还要提一下信号量和队列的另一个差别,那就是通用是塞东西,如果队列满了暂时塞不了,那么会有等待时间(进入阻塞),等待塞进去。而信号量加一超过了上限,那么直接返回错误,而不会进入阻塞等待能够加一。

信号量比较简单就不演示了,下一个介绍一下互斥锁。

头文件跟信号量是同一个,它们也有很多相似的地方,使用起来基本一样,创建——加一(上锁)——减一(开锁)——销毁。

甚至在一般情况下,我们可以把互斥锁看作是二进制信号量来使用。

互斥锁的开锁(give)和上锁(take)以及销毁用的函数都和信号量是一样的,因此我们在额外介绍一下互斥锁的创建以及互斥锁和二进制信号量的差别即可。

下面这个宏创建互斥锁。

xSemaphoreCreateMutex()

从下面这个官方示例可以得知互斥锁的句柄类型和信号量是一样的。

点开这个宏往上翻一翻可以发现,互斥锁,信号量,队列本质上是一个玩意,不过在创建的时候给我们默认加了个参数来指定类型为互斥锁,差别就在这边了。

在应用层上我们有个规定,那就是谁上锁谁开锁,而不能任务A上锁,然后让任务B开锁,这也是互斥锁和信号量的差别。

信号量更适用于控制数量有多个的资源,而互斥锁更适用于控制只有一个的核心资源。

假设我们只有一个核心资源,而任务A和任务B都需要用到,我们使用互斥锁来保证核心资源的使用,那么我们应该在任务中做到在使用前上锁,保证在使用过程中不会被其他任务干扰,在使用后开锁,保证之后能让其他任务使用。

互斥锁在使用上take和give是在同一个任务中使用的,而我们之前使用信号量,基本上take和give是分任务使用的。

一个简单的小例子大家就懂了。

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
SemaphoreHandle_t mutex;void test1(void*) {while (1) {xSemaphoreTake(mutex,0);// 假设在这边使用核心资源xSemaphoreGive(mutex);vTaskDelay(pdMS_TO_TICKS(1000));}
}void test2(void*) {while (1) {xSemaphoreTake(mutex,0);// 假设在这边使用核心资源xSemaphoreGive(mutex);vTaskDelay(pdMS_TO_TICKS(1000));}
}void app_main(void) {TaskHandle_t test1_handle;TickType_t curTime = xTaskGetTickCount();mutex = xSemaphoreCreateMutex();xTaskCreate(test1, "test1", 1024 * 2, NULL, configMAX_PRIORITIES /2,&test1_handle);xTaskCreate(test2, "test2", 1024 * 2, NULL, configMAX_PRIORITIES /2,NULL);while (1) {xTaskDelayUntil(&curTime, pdMS_TO_TICKS(1000));}
}

那么大家或许会有疑问,既然信号量和互斥锁是一个东西(句柄类型一样,不过其实在底层有不同),只是在使用层面上不同,那为什么要有互斥锁呢?我用二进制信号量貌似也能代替互斥锁。

那我们想象一个场景,我们创建了很多优先级不一样的任务,优先级高的任务更容易拿到时间片,也就更容易执行,如果高优先级任务和低优先级任务共用一个核心资源,并且我们使用二进制信号量来控制,当低优先级任务使用核心资源的时候,将信号量减一,高优先级任务想要使用这个核心资源的时候就只能等着,但是由于低优先级任务执行的概率比较低,因此尽管高优先级的优先级比较高,但还是得不到执行,这就导致了“饥饿”现象。

而互斥锁有个优先级继承机制,当高优先级任务请求一个被低优先级任务持有的互斥锁时,系统会将持有锁的低优先级任务的优先级临时提升到与高优先级任务相同的级别。这样,持有锁的任务能够更快地完成其执行并释放锁,从而使高优先级任务能够继续执行。

这就是互斥锁和信号量在本质上的差别。

互斥锁更适合用于需要严格互斥访问共享资源的场景。由于具有优先级继承机制,它能够在一定程度上减少因任务优先级不同而导致的死锁或延迟问题。

而二进制信号量更适用于任务间或任务与中断间的同步。例如,当一个中断发生时,可以通过释放一个二进制信号量来通知一个或多个任务进行相应处理。由于二进制信号量相对简单且没有优先级继承机制的开销,因此它在某些同步场景中可能更高效。

这篇关于【ESP-IDF FreeRTOS】信号量的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1123889

相关文章

FreeRTOS-基本介绍和移植STM32

FreeRTOS-基本介绍和STM32移植 一、裸机开发和操作系统开发介绍二、任务调度和任务状态介绍2.1 任务调度2.1.1 抢占式调度2.1.2 时间片调度 2.2 任务状态 三、FreeRTOS源码和移植STM323.1 FreeRTOS源码3.2 FreeRTOS移植STM323.2.1 代码移植3.2.2 时钟中断配置 一、裸机开发和操作系统开发介绍 裸机:前后台系

FreeRTOS内部机制学习03(事件组内部机制)

文章目录 事件组使用的场景事件组的核心以及Set事件API做的事情事件组的特殊之处事件组为什么不关闭中断xEventGroupSetBitsFromISR内部是怎么做的? 事件组使用的场景 学校组织秋游,组长在等待: 张三:我到了 李四:我到了 王五:我到了 组长说:好,大家都到齐了,出发! 秋游回来第二天就要提交一篇心得报告,组长在焦急等待:张三、李四、王五谁先写好就交谁的

FreeRTOS学习笔记(六)队列

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、队列的基本内容1.1 队列的引入1.2 FreeRTOS 队列的功能与作用1.3 队列的结构体1.4 队列的使用流程 二、相关API详解2.1 xQueueCreate2.2 xQueueSend2.3 xQueueReceive2.4 xQueueSendFromISR2.5 xQueueRecei

FreeRTOS学习笔记(二)任务基础篇

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、 任务的基本内容1.1 任务的基本特点1.2 任务的状态1.3 任务控制块——任务的“身份证” 二、 任务的实现2.1 定义任务函数2.2 创建任务2.3 启动任务调度器2.4 任务的运行与切换2.4.1 利用延时函数2.4.2 利用中断 2.5 任务的通信与同步2.6 任务的删除2.7 任务的通知2

FreeRTOS学习笔记(四)Freertos的中断管理及临界保护

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、Cortex-M 中断管理1.1 中断优先级分组1.2 相关寄存器1.3 相关宏定义1.4 FreeRTOS 开关中断 二、临界段及其保护2.1 taskENTER_CRITICAL( ) 和 taskEXIT_CRITICAL( )2.2 taskENTER_CRITICAL_FROM_ISR( )

WIN11 ESP32 IDF + VSCODE 环境搭建[教程向]

前言 目录 前言 安装ESP32-IDF VSCODE插件安装 编译测试 很多时候我们想学习一门新的技能,需要使用全新的开发环境,很多时候我们会在安装环境这个环节卡住很久,这里简单介绍一下ESP32+VSCODE环境搭建。 安装ESP32-IDF https://dl.espressif.cn/dl/esp-idf/?idf=4.4 直接复制上面链接,进入idf下载界面。

龙芯+FreeRTOS+LVGL实战笔记(新)——05部署主按钮

本专栏是笔者另一个专栏《龙芯+RT-Thread+LVGL实战笔记》的姊妹篇,主要的区别在于实时操作系统的不同,章节的安排和任务的推进保持一致,并对源码做了改进和优化,各位可以先到本人主页下去浏览另一专栏的博客列表(目前已撰写36篇,图1所示),再决定是否订阅。此外,也可以前往本人在B站的视频合集(图2所示)观看所有演示视频,合集首个视频链接为: 借助RT-Thread和LVGL

Linux多线程——POSIX信号量与环形队列版本之生产消费模型

文章目录 POSIX信号量POSIX的操作初始化销毁等待信号量(申请资源)发布信号量(放下资源) 环形队列之生产消费模型 POSIX信号量 POSIX信号量和System V信号量是不同的标准 但是实现的功能是一样的,都是为了解决同步的问题 我们说信号量指的就是资源的数量 在生产者与消费者模型里面,生产者与消费者所认为的资源是不同的 生产者认为空间是资源,因为每次都要

信号与信号量的区别[转]

信号量(Semaphore),有时被称为信号灯,是在多环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。为了完成这个过程,需要创建一个信号量VI,然后将Acquire Semaphore VI以及Release Se

FreeRTOS学习笔记—④RTOS通信管理篇/同步互斥与通信(正在更新中)

二、RTOS的核心功能   RTOS的核心功能块主要分为任务管理、内核管理、时间管理以及通信管理4部分,框架图如下所示:   (1)任务管理:负责管理和调度任务的执行,确保系统中的任务能够按照预期运行。   (2)内核管理:负责系统核心功能的管理,包括内存、中断、异常处理和系统启动等。   (3)时间管理:负责所有与时间相关的操作,包括系统时钟、定时器、任务延迟和周期性任务的执行。   (4)通