FreeRTOS之队列上锁和解锁(详解)

 

这篇文章将记录我学习实时操作系统FreeRTOS的队列上锁和解锁的知识,在此分享给大家,希望我的分享能给你带来不一样的收获!

目录

一、简介

 二、队列上锁函数prvLockQueue()

1、函数初探

2、应用示例 

 三、队列解锁函数prvUnLockQueue() 

1、函数初探及详细注释

详细注释解释:

结论:

2、函数使用示例


一、简介

在FreeRTOS中,队列是一种用于在任务之间传递数据的通信机制它可以实现生产者任务将数据发送到队列中,然后消费者任务从队列中接收数据。队列的上锁和解锁操作是用来保护队列数据的完整性和一致性的。

当一个任务要向队列发送数据时,首先需要对队列进行上锁操作。这是为了防止其他任务同时访问队列,从而导致数据的错误读写。在上锁期间,其他任务无法访问队列,直到上锁任务完成发送操作并解锁队列。

类似地,当一个任务要从队列接收数据时,也需要对队列进行上锁操作。这是为了保证在接收数据的过程中,队列中的数据不会被其他任务修改。在上锁期间,其他任务无法修改队列中的数据,直到上锁任务完成接收操作并解锁队列。

通过对队列进行上锁和解锁操作,可以确保在多任务环境下,队列的数据操作是安全和可靠的。

 二、队列上锁函数prvLockQueue()

1、函数初探

/*-----------------------------------------------------------*//** Macro to mark a queue as locked.  Locking a queue prevents an ISR from* accessing the queue event lists.*/
#define prvLockQueue( pxQueue )								\taskENTER_CRITICAL();									\{														\if( ( pxQueue )->cRxLock == queueUNLOCKED )			\{													\( pxQueue )->cRxLock = queueLOCKED_UNMODIFIED;	\}													\if( ( pxQueue )->cTxLock == queueUNLOCKED )			\{													\( pxQueue )->cTxLock = queueLOCKED_UNMODIFIED;	\}													\}														\taskEXIT_CRITICAL()
/*-----------------------------------------------------------*/

就是将队列中的成员变量cRxLock和cTxlock设置为queueLOCKED_UNMODIFIED就行

2、应用示例 

在FreeRTOS中,队列并不需要显式的上锁(或者说信号量),因为队列的操作已经在内部进行了同步和互斥处理。这是因为FreeRTOS的队列实现是线程安全的,它在底层使用了信号量和互斥量来确保多任务环境下的安全访问。

下面是一个简单的示例,演示了如何在FreeRTOS中创建、发送和接收队列消息:

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"#define QUEUE_LENGTH    5
#define ITEM_SIZE       sizeof(int)QueueHandle_t xQueue;void SenderTask(void *pvParameters) {int count = 0;while (1) {// 将消息发送到队列xQueueSend(xQueue, &count, portMAX_DELAY);count++;// 延时一段时间vTaskDelay(pdMS_TO_TICKS(1000));}
}void ReceiverTask(void *pvParameters) {int received_value;while (1) {// 从队列接收消息if (xQueueReceive(xQueue, &received_value, portMAX_DELAY) == pdPASS) {// 处理接收到的消息printf("Received: %d\n", received_value);}}
}int main(void) {// 创建队列xQueue = xQueueCreate(QUEUE_LENGTH, ITEM_SIZE);// 创建发送任务xTaskCreate(SenderTask, "Sender", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);// 创建接收任务xTaskCreate(ReceiverTask, "Receiver", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);// 启动调度器vTaskStartScheduler();// 如果调度器启动失败,进入错误处理for (;;);return 0;
}

 (1)、队列创建:main 函数中创建了一个队列,它可以容纳 QUEUE_LENGTHint 类型的元素。

xQueue = xQueueCreate(QUEUE_LENGTH, ITEM_SIZE);

(2)、 发送任务 (SenderTask):SenderTask 函数中通过 xQueueSendcount 的值发送到队列中,然后 count 递增,并延时1秒钟。

void SenderTask(void *pvParameters) {
    int count = 0;

    while (1) {
        xQueueSend(xQueue, &count, portMAX_DELAY);
        count++;
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

(3)、 接收任务 (ReceiverTask):ReceiverTask 函数中通过 xQueueReceive 从队列中接收消息,并将接收到的消息打印出来。

void ReceiverTask(void *pvParameters) {
    int received_value;

    while (1) {
        if (xQueueReceive(xQueue, &received_value, portMAX_DELAY) == pdPASS) {
            printf("Received: %d\n", received_value);
        }
    }
}

 (4)、主函数 (main):

  • 创建了队列 xQueue
  • 创建了发送任务 SenderTask 和接收任务 ReceiverTask
  • 启动了 FreeRTOS 的任务调度器 vTaskStartScheduler()

 三、队列解锁函数prvUnLockQueue() 

1、函数初探及详细注释

/*-----------------------------------------------------------*/static void prvUnlockQueue( Queue_t * const pxQueue )
{/* 必须在调度器挂起的情况下调用此函数 *//* 锁计数器包含在队列被锁定期间添加或移除的额外数据项数量。当队列被锁定时,可以添加或移除项目,但不能更新事件列表。 */taskENTER_CRITICAL();{int8_t cTxLock = pxQueue->cTxLock;/* 检查在队列被锁定期间是否有数据被添加 */while( cTxLock > queueLOCKED_UNMODIFIED ){/* 当队列被锁定时有数据被发送。有任务因为数据可用而阻塞吗? */#if ( configUSE_QUEUE_SETS == 1 ){if( pxQueue->pxQueueSetContainer != NULL ){if( prvNotifyQueueSetContainer( pxQueue, queueSEND_TO_BACK ) != pdFALSE ){/* 队列是队列集的成员,并且向队列集发送数据导致更高优先级的任务解除阻塞。需要进行上下文切换。 */vTaskMissedYield();}else{mtCOVERAGE_TEST_MARKER();}}else{/* 从事件列表中移除的任务将被添加到等待准备列表中,因为调度器仍处于挂起状态。 */if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ){if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ){/* 等待的任务优先级更高,所以需要记录需要进行上下文切换。 */vTaskMissedYield();}else{mtCOVERAGE_TEST_MARKER();}}else{break;}}}#else /* configUSE_QUEUE_SETS */{/* 从事件列表中移除的任务将被添加到等待准备列表中,因为调度器仍处于挂起状态。 */if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ){if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ){/* 等待的任务优先级更高,所以需要记录需要进行上下文切换。 */vTaskMissedYield();}else{mtCOVERAGE_TEST_MARKER();}}else{break;}}#endif /* configUSE_QUEUE_SETS */--cTxLock;}pxQueue->cTxLock = queueUNLOCKED;}taskEXIT_CRITICAL();/* 处理接收锁(cRxLock)的解锁 */taskENTER_CRITICAL();{int8_t cRxLock = pxQueue->cRxLock;while( cRxLock > queueLOCKED_UNMODIFIED ){/* 检查在队列被锁定期间是否有数据被移除 */if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ){if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ){vTaskMissedYield();}else{mtCOVERAGE_TEST_MARKER();}--cRxLock;}else{break;}}pxQueue->cRxLock = queueUNLOCKED;}taskEXIT_CRITICAL();
}
/*-----------------------------------------------------------*/

详细注释解释:

  1. 任务关键性操作管理:

    • 使用 taskENTER_CRITICAL() 进入临界区,确保后续操作的原子性和避免抢占。
    • 使用 taskEXIT_CRITICAL() 退出临界区,保证对队列锁定状态的修改是原子的。
  2. 解锁发送 (cTxLock):

    • 检查在队列被锁定期间是否有数据被添加 (cTxLock >queueLOCKED_UNMODIFIED)。
    • 根据是否使用队列集 (configUSE_QUEUE_SETS),通知相关的队列集容器或者从等待接收数据的任务事件列表 (xTasksWaitingToReceive) 中移除任务。
    • 如果由于数据可用导致更高优先级任务解除阻塞,调用 vTaskMissedYield() 进行上下文切换。
  3. 解锁接收 (cRxLock):

    • 检查在队列被锁定期间是否有数据被移除 (cRxLock >queueLOCKED_UNMODIFIED)。
    • 从等待发送数据的任务事件列表 (xTasksWaitingToSend) 中移除任务,如果任务因为数据发送而变得可运行,调用 vTaskMissedYield() 进行上下文切换。
  4. 测试覆盖率:

    • 使用 mtCOVERAGE_TEST_MARKER() 调用来确保测试所有代码路径。

结论:

这段代码在实时操作系统(RTOS)环境中非常重要,用于管理多任务对共享数据结构(如队列)的同步访问。通过管理锁计数和任务准备就绪状态,确保在队列解锁后,等待访问队列的任务可以及时执行,以提高系统效率和响应能力。

2、函数使用示例

假设我们有一个队列 xQueue,在实际应用中可能是用于任务之间传递数据的队列。以下是一个示例代码:

#include <stdio.h>
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"/* 定义一个队列 */
#define QUEUE_LENGTH    5
#define ITEM_SIZE       sizeof(int)QueueHandle_t xQueue;void vSenderTask(void *pvParameters)
{int i = 0;BaseType_t xStatus;while (1){/* 发送数据到队列 */xStatus = xQueueSend(xQueue, &i, portMAX_DELAY);if (xStatus != pdPASS){printf("Failed to send to queue!\n");}else{printf("Sent: %d\n", i);}i++;vTaskDelay(pdMS_TO_TICKS(500));}
}void vReceiverTask(void *pvParameters)
{int rxItem;BaseType_t xStatus;while (1){/* 接收数据 */xStatus = xQueueReceive(xQueue, &rxItem, portMAX_DELAY);if (xStatus == pdPASS){printf("Received: %d\n", rxItem);}else{printf("Failed to receive from queue!\n");}vTaskDelay(pdMS_TO_TICKS(1000));}
}int main(void)
{/* 创建队列 */xQueue = xQueueCreate(QUEUE_LENGTH, ITEM_SIZE);/* 创建发送任务 */xTaskCreate(vSenderTask, "Sender", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);/* 创建接收任务 */xTaskCreate(vReceiverTask, "Receiver", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 2, NULL);/* 启动调度器 */vTaskStartScheduler();/* 永远不应该运行到这里 */return 0;
}
  • vSenderTask 和 vReceiverTask 分别是发送和接收任务,用于往队列发送数据和从队列接收数据。
  • xQueueSend 和 xQueueReceive 函数用于操作队列,这些函数会在内部调用队列的管理函数,其中包括了类似 prvUnlockQueue 的操作,确保在正确的时机解锁队列以便其他任务可以访问它。

这段示例代码展示了如何使用队列在 FreeRTOS 环境中进行任务间通信,并在发送和接收数据时自动处理队列的锁定和解锁,确保数据的正确传递和任务的及时响应。

四、结语

关于FreeRTOS的队列上锁和解锁的知识就分享至此,希望我的分享对你有所帮助!

 

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

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

相关文章

第8天:模块和包

学习目标 理解Python模块和包的概念学习如何创建和导入模块掌握标准库模块的使用学习如何使用包组织代码 学习内容 1. 模块的概念 模块是一个包含Python代码的文件&#xff0c;模块可以包含函数、类和变量&#xff0c;也可以包含可执行的代码。模块使你能够组织和重用代码。…

如何对关键字段进行脱敏(二)如何在某些查询中取消脱敏?

要在使用Mybatis-plus进行数据库查询时准确区分哪些查询需要脱敏,哪些不需要,同时保留获取精确手机号码的能力,可以采用以下方案: 自定义注解标记 创建一个自定义注解,用于标记不需要脱敏的查询方法: Retention(RetentionPolicy.RUNTIME) Target(ElementType.METHOD) public…

TCP/IP模型每层内容和传输单位

TCP/IP&#xff08;Transmission Control Protocol/Internet Protocol&#xff09;模型是一种用于描述网络通信中协议层次结构的模型&#xff0c;它最初被设计用来描述互联网的协议栈。TCP/IP模型通常分为四层&#xff0c;自下而上分别为&#xff1a; 网络接入层&#xff08;Ne…

js之模糊搜索

多的不说 少的不唠 直接上代码

使用el-form自定义校验的同时使用v-if/v-show引起的校验问题

有时候需要根据不同的条件来回显具体的内容&#xff0c;这时候就会用到v-if/v-show&#xff0c;此时表单的校验就可能会出现一些问题。 比如&#xff1a;使用v-if的时候&#xff0c;明明切换到了另一个条件&#xff0c;但是对应元素的表单校验却没有生效&#xff1b;使用v-show…

警翼警用记录仪视频格式化后恢复方法

警翼是国内较大的一家警用记录仪厂商&#xff0c;此品牌我们恢复过很多&#xff0c;此次遇到的是一个典型的误格式化的情况&#xff0c;我们来看看误格式化后如何恢复。 故障存储: 32G卡/fat32 故障现象: 客户提供的信息是在交接设备后没有及时备份而做出了初始化设备的操…

养老院管理系统-计算机毕业设计源码00010

养老院管理系统的设计与实现 摘要 本文介绍了一种基于Spring Boot框架的养老院管理系统的设计与实现。该系统旨在帮助养老院管理者更有效地管理机构内的各项事务&#xff0c;并提供更好的服务于老年人。系统的设计考虑了养老院管理的特殊需求&#xff0c;包括系统用户、老人信息…

高二的他已通过NOI保送北大了,让我们一起了解他的信息学奥赛学习经历吧!!!

相信关注本号的各位&#xff0c;对于信息学奥赛已经不陌生了&#xff0c;部分同学也已经开始踏入信息学的旅程&#xff0c;但前路茫茫&#xff0c;让我们一起看看已经取得成就的同学的经历吧。 今天要介绍的这位同学&#xff0c;是来自深圳中学的高二某班的欧阳达晟同学&#x…

简洁纯文字类的Typecho主题wenso

主题介绍 文章说说类博客网站源码&#xff0c;页面清新简洁。适合文章说说美文博客网站建站使用&#xff0c;响应式手机版本。 本来是dedecms的模板&#xff0c;也比较简单&#xff0c;适合用来搭建一个文学类的&#xff0c;纯文字的网站&#xff0c;简单的改成了typecho&…

AI写作开启新篇章

AI写作开启新篇章 随着人工智能技术的飞速发展&#xff0c;AI写作逐渐成为一种新的趋势和工具。它不仅为个人写作提供了便利&#xff0c;也在企业内容生产、学术研究、创意写作等领域展现了广泛的应用前景。AI写作的核心在于利用自然语言处理&#xff08;NLP&#xff09;技术&…

3086. 拾起 K 个 1 需要的最少行动次数 Hard

给你一个下标从 0 开始的二进制数组 nums&#xff0c;其长度为 n &#xff1b;另给你一个 正整数 k 以及一个 非负整数 maxChanges 。 Alice 在玩一个游戏&#xff0c;游戏的目标是让 Alice 使用 最少 数量的 行动 次数从 nums 中拾起 k 个 1 。游戏开始时&#xff0c;Alice 可…

JVM 堆内存结构 年轻代 老年代

堆内存 内存划分 对于大多数应用&#xff0c;Java 堆是 Java 虚拟机管理的内存中最大的一块&#xff0c;被所有线程共享。此内存区域的唯一目的就是存放对象实例&#xff0c;几乎所有的对象实例以及数据都在这里分配内存。 为了进行高效的垃圾回收&#xff0c;虚拟机把堆内存…

线程池的七大核心参数

今天了解到线程池的七大核心参数包括&#xff1a; 1. 核心线程数&#xff08;corePoolSize&#xff09;&#xff1a;线程池中能够同时执行的线程数量。 2. 最大线程数&#xff08;maximumPoolSize&#xff09;&#xff1a;线程池中允许的最大线程数量。 3. 线程空闲时间&#…

小白 | 华为云docker设置镜像加速器

一、操作场景 通过docker pull命令下载镜像中心的公有镜像时&#xff0c;往往会因为网络原因而需要很长时间&#xff0c;甚至可能因超时而下载失败。为此&#xff0c;容器镜像服务提供了镜像下载加速功能&#xff0c;帮助您获得更快的下载体验。 二、约束与限制 构建镜像的客…

6年铲屎官测评宠物空气净化器哪款好,热门养宠空气净化器排名

作为一名资深猫奴&#xff0c;发现很多铲屎官每到春秋换季就开始疯狂打喷嚏、突然开始全身过敏。其原因是猫毛一到换季就开始疯狂掉毛&#xff0c;相对于可见猫毛&#xff0c;漂浮在空气中的浮毛就是罪灰祸首。微小的浮毛在空气总容易被人体吸入体内&#xff0c;而浮毛上面附带…

【Python机器学习】模型评估与改进——在模型选择中使用评估指标

我们通常希望&#xff0c;在使用GridSearchCV或cross_val_score进行模型选择时能够使用AUC等指标。scikit-learn提供了一种非常简单的实现方法&#xff0c;那就是scoring参数&#xff0c;它可以同时用于GridSearchCV和cross_val_score。你只需要提供一个字符串&#xff0c;用于…

Appium自动化测试框架3

滑动与拖拽 swipe 滑动时间的长短会影响最后的结果的 是有一定误差的 from appium import webdriver import time # 启动一个字典 包装相应的启动参数 desired_caps dict() # 平台的名字&#xff0c;安卓还是IOS 大小写无所谓 desired_caps[platformName] Android # 平台的…

软考(高项)系统分析师--开篇

文章目录 前言一、学习过程&#xff1a;二、考试过程&#xff1a;总结: 前言 博客主在2024年05月22号参加了高项的系统分析师考试&#xff0c;在此进行一个经验总结以供小伙伴参考,如果想参考博客主学习资料的可以在下方留言进行交流。 一、学习过程&#xff1a; 先说下时间&…

2.5 C#视觉程序开发实例1----设计一个IO_Manager

2.5 C#视觉程序开发实例1----设计一个IO_Manager 第一步目标&#xff1a; 1 实现获取IO触发信号Trig0 2 能够实现程序切换 3 图像处理后能够输出一个脉冲 1 IO 引脚定义 1.1 输入信号定义 1.2 输出信号定义 2 IO时序图 2.1 触发时序 2.2 切换程序时序图 3 IO_Manager.cs …