任务通信过程中,如果消息类型不同,使用一条队列来实现则有些麻烦。

FreeRTOS 提供队列集合,用于对多个队列以及信号量进行“监听”,只要其中不管哪一个有消息到来,都可以让任务退出阻塞状态。这就类似于linux网络编程时的select(IO复用)。

先看一下队列结构体

多了一个pxQueueSetContainer成员变量,队列所属队列集。

在插入队列项的时候,用于查找队列所属的队列集并通知正在监听该队列集的任务。

/* 队列结构体 */
typedef struct QueueDefinition
{int8_t *pcHead;            /* 队列存储区头部,即第一个队列项 */int8_t *pcWriteTo;  /* 队列项插入指针 */union{QueuePointers_t xQueue;              /* 队列 */SemaphoreData_t xSemaphore;     /* 信号量 */}u;List_t xTasksWaitingToSend;         /* 等待发送队列项而阻塞的任务列表 */List_t xTasksWaitingToReceive; /* 等待接收队列项而阻塞的任务列表 */volatile UBaseType_t uxMessagesWaiting;    /* 已经插入队列项个数 */UBaseType_t uxLength;        /* 队列项存储区最多队列项个数 */UBaseType_t uxItemSize;  /* 每个队列项大小 */volatile int8_t cRxLock;   /* 锁定期间,从队列中接收队列项的次数 */volatile int8_t cTxLock;  /* 锁定期间,向队列中发送队列项的次数 */......#if (configUSE_QUEUE_SETS == 1)struct QueueDefinition *pxQueueSetContainer;   /* 队列所属队列集 */#endif......
}xQUEUE;
typedef xQUEUE Queue_t;

创建队列集

队列集本身也是一个队列,将所有队列加入队列集,就可以对所有队列进行监听了

/* 创建队列集 */
QueueSetHandle_t xQueueCreateSet(const UBaseType_t uxEventQueueLength)
{QueueSetHandle_t pxQueue;/* 创建队列项大小为sizeof(Queue_t *),长度为uxEventQueueLength的队列 */pxQueue = xQueueGenericCreate(uxEventQueueLength, (UBaseType_t)sizeof(Queue_t *), queueQUEUE_TYPE_SET);return pxQueue;
}

将队列加入队列集

将队列加入队列集,本质上就是将队列的所属队列集设置为该队列集

/* 将队列加入队列集 */
BaseType_t xQueueAddToSet(QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet)
{BaseType_t xReturn;/* 进入临界区 */taskENTER_CRITICAL();{/* 队列所属队列集不为空 */if(((Queue_t *)xQueueOrSemaphore)->pxQueueSetContainer != NULL){/* 返回错误 */xReturn = pdFAIL;}/* 队列中已经有队列项 */else if(((Queue_t *)xQueueOrSemaphore)->uxMessagesWaiting != (UBaseType_t)0){/* 返回错误 */xReturn = pdFAIL;}/* 队列不属于任何队列集,队列中没有队列项 */else{/* 将队列所属队列集设置该队列集 */((Queue_t *)xQueueOrSemaphore)->pxQueueSetContainer = xQueueSet;/* 返回成功 */xReturn = pdPASS;}}/* 退出临界区 */taskEXIT_CRITICAL();return xReturn;
}

发送队列项

和普通队列不同的是:

如果队列被加入队列集,则通知队列集

如果队列没有加入队列集,则通知队列(解除那些等待的队列)

注意:某条队列中如果已经存在队列项,再插入一个队列项的时候并不会再通知队列集,这就意味着监听到某条队列有队列项的时候,要一次性把队列取干净

/* 发送队列项 */
BaseType_t xQueueGenericSend(QueueHandle_t xQueue, const void *const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition)
{BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;TimeOut_t xTimeOut;Queue_t *const pxQueue = xQueue;configASSERT(pxQueue);configASSERT(!((pvItemToQueue == NULL) && (pxQueue->uxItemSize != (UBaseType_t)0U)));configASSERT(!((xCopyPosition == queueOVERWRITE) && (pxQueue->uxLength != 1)));#if ((INCLUDE_xTaskGetSchedulerState == 1) || (configUSE_TIMERS == 1)){configASSERT(!((xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED) && (xTicksToWait != 0)));}#endiffor(;;){/* 进入临界区 */taskENTER_CRITICAL();{/* 目前已插入队列项数小于最大可插入队列数或者覆盖型插入 */if((pxQueue->uxMessagesWaiting < pxQueue->uxLength) || (xCopyPosition == queueOVERWRITE)){traceQUEUE_SEND(pxQueue);#if (configUSE_QUEUE_SETS == 1){UBaseType_t uxPreviousMessagesWaiting = pxQueue->uxMessagesWaiting;/* 将数据拷贝到队列项中 */xYieldRequired = prvCopyDataToQueue(pxQueue, pvItemToQueue, xCopyPosition);/* 该队列被加入队列集 */if(pxQueue->pxQueueSetContainer != NULL){/* 覆盖型插入或者先前队列项数不为0,说明已经通知过队列集 */if((xCopyPosition == queueOVERWRITE) && (uxPreviousMessagesWaiting != (UBaseType_t)0)){mtCOVERAGE_TEST_MARKER();}/* 通知队列集 */else if(prvNotifyQueueSetContainer(pxQueue, xCopyPosition) != pdFALSE){queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}/* 该队列没有被加入队列集 */else{/* 等待接收队列项而阻塞的任务列表不为空 */if(listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToReceive)) == pdFALSE){/* 将任务从事件列表中移除一个任务,并挂接到就绪列表 */if(xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToReceive)) != pdFALSE){/* 请求切换任务 */queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}/* 等待接收队列项而阻塞的任务列表为空 */else if(xYieldRequired != pdFALSE){queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}}#else{......}#endif/* 退出临界区 */taskEXIT_CRITICAL();/* 成功 */return pdPASS;}/* 目前队列已经满了,且不是覆盖型插入 */else{/* 阻塞时间为0 */if(xTicksToWait == (TickType_t)0){taskEXIT_CRITICAL();traceQUEUE_SEND_FAILED(pxQueue);/* 返回队列已满错误 */return errQUEUE_FULL;}/* 当前节拍状态还未记录 */else if(xEntryTimeSet == pdFALSE){/* 记录当前节拍状态 */vTaskInternalSetTimeOutState(&xTimeOut);/* 当前节拍状态已经记录 */xEntryTimeSet = pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}}}/* 退出临界区 */taskEXIT_CRITICAL();/* 挂起调度器 */vTaskSuspendAll();/* 锁定队列 */prvLockQueue(pxQueue);/* 检查任务是否超时,并未超时 */if(xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait) == pdFALSE){/* 检查队列是否已满,已经满了 */if(prvIsQueueFull(pxQueue) != pdFALSE){traceBLOCKING_ON_QUEUE_SEND(pxQueue);/* 将任务挂接到等待发送而阻塞的任务列表中,并将任务挂接到延时列表中 */vTaskPlaceOnEventList(&(pxQueue->xTasksWaitingToSend), xTicksToWait);/* 解锁队列 */prvUnlockQueue(pxQueue);/* 解除调度器挂起 */if(xTaskResumeAll() == pdFALSE){/* 请求切换 */portYIELD_WITHIN_API();}}/* 刚好队列出现空位,下一次while循环重新插入 */else{/* 解锁队列 */prvUnlockQueue(pxQueue);/* 解除调度器挂起 */(void)xTaskResumeAll();}}/* 已经超时或者超时之后 */else{/* 解锁队列 */prvUnlockQueue(pxQueue);/* 解除调度器挂起 */(void)xTaskResumeAll();traceQUEUE_SEND_FAILED(pxQueue);/* 队列已满 */return errQUEUE_FULL;}}
}

源码中使用prvNotifyQueueSetContainer函数,通知队列集

通知队列集,其实就是将队列作为队列项插入到队列集中,并通知监听而阻塞的任务

/* 通知队列集 */
static BaseType_t prvNotifyQueueSetContainer(const Queue_t *const pxQueue, const BaseType_t xCopyPosition)
{Queue_t *pxQueueSetContainer = pxQueue->pxQueueSetContainer;BaseType_t xReturn = pdFALSE;configASSERT(pxQueueSetContainer);configASSERT(pxQueueSetContainer->uxMessagesWaiting < pxQueueSetContainer->uxLength);/* 队列集还没满 */if(pxQueueSetContainer->uxMessagesWaiting < pxQueueSetContainer->uxLength){const int8_t cTxLock = pxQueueSetContainer->cTxLock;traceQUEUE_SEND(pxQueueSetContainer);/* 将队列拷贝进队列集 */xReturn = prvCopyDataToQueue(pxQueueSetContainer, &pxQueue, xCopyPosition);/* 没锁定 */if(cTxLock == queueUNLOCKED){/* 有任务在监听队列集 */if(listLIST_IS_EMPTY(&(pxQueueSetContainer->xTasksWaitingToReceive)) == pdFALSE){/* 将正在监听队列集而阻塞的任务从事件列表中移除,加入就绪列表 */if(xTaskRemoveFromEventList(&(pxQueueSetContainer->xTasksWaitingToReceive)) != pdFALSE){xReturn = pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}/* 锁定 */else{/* 锁定期间发送次数加一,解锁的时候补偿处理这么多次 */pxQueueSetContainer->cTxLock = (int8_t)(cTxLock + 1);}}else{mtCOVERAGE_TEST_MARKER();}return xReturn;
}

监听队列集

监听队列集,其实就是等待从队列集中取出队列指针

/* 监听队列集 */
QueueSetMemberHandle_t xQueueSelectFromSet(QueueSetHandle_t xQueueSet, TickType_t const xTicksToWait)
{QueueSetMemberHandle_t xReturn = NULL;/* 从队列集中取出队列指针 */(void)xQueueReceive((QueueHandle_t)xQueueSet, &xReturn, xTicksToWait);return xReturn;
}

FreeRTOS队列集相关推荐

  1. FreeRTOS队列原理

    1. 队列 队列是为了任务与任务.任务与中断之间通信而准备的,可以在任务与任务.任务与中断之间传递消息,队列中可以存储有限的.大小固定的数据项.创建队列的时候需要指定数据项目的大小和队列的长度. 1. ...

  2. 消息队列——RabbitMQ消息队列集群

    RabbitMQ消息队列集群 消息队列/中间件 RabbitMQ详解 RabbitMQ单机部署 RabbitMQ集群部署 消息队列/中间件 一.前言 在我们秒杀抢购商品的时候,系统会提醒我们稍等排队中 ...

  3. FreeRTOS队列

    在实际应用中,我们会遇到一个任务或者中断服务需要和另一个任务进行消息传递,FreeRTOS提供了队列的机制来完成任务与任务.任务与中断之间的消息传递. 0x01 队列简介 队列是为了任务与任务.任务与 ...

  4. 亲测可用,超详细RabbitMQ消息队列集群配置

    RabbitMQ是什么? MQ(Message Queue,消息队列)消息中间件,一般以集群方式部署,主要提供消息的接受和发送,实现各微服务之间的消息同步. 原理介绍 rabbitmq是依据erlan ...

  5. RabbitMQ消息队列集群

    RabbitMQ MQ(Message Queue,消息队列)是一款消息中间件,一般以集群方式部署,主要提供消息的接受和发送,实现各微服务之间的消息异步. 集群原理 rabbitmq 是依据erlan ...

  6. FreeRTOS 队列管理

      基于 FreeRTOS 的应用程序由一组独立的任务构成--每个任务都是具有独立权限的小程序.这些独立的任务之间很可能会通过相互通信以提供有用的系统功能.FreeRTOS 中所有的通信与同步机制都是 ...

  7. RabbitMQ#RabbitMQ+Haproxy消息队列集群和代理部署

    文章目录 一.消息队列/中间件 1.RabbitMQ本质上起到的作用就是削峰填谷 2.MQ简介(RabbitMQ比Kafka) 3.MQ消息队列的分类 二.RabbitMQ介绍(端口15672) 1. ...

  8. 2018 支付宝Java开发四面:Ngnix+MQ队列+集群+并发抢购

    一面 介绍项目 java 线程池的实现原理,threadpoolexecutor关键参数解释 hashmap的原理,容量为什么是2的幂次 为什么要同时重写hashcode和equals Concurr ...

  9. freeRtos学习笔(4)消息队列

    freeRtos学习笔记 freeRtos消息队列 为什么要用消息队列 消息队列可以在任务与任务间,中断与任务间传递信息.为什么不用全局数组?全局数组也可以传递信息,但是和消息队列相比,消息队列有一下 ...

最新文章

  1. log parser 微软iis 日志分析
  2. 如何模拟将CPU、IO打满?
  3. Day01 你如何保持健康
  4. c++之趣味new代码大家看
  5. 二分图最大匹配的König定理及其证明
  6. elon函数_Neuroink Elon麝香制造中的灾难
  7. HTTPS证书的申请过程
  8. CentOS 7.X 升级 Python 3.8.X
  9. oracle创建简单包,Oracle创建程序包是什么?
  10. java构造器_Java类加载的过程
  11. NAS、DAS和SAN三种存储究竟是什么?
  12. poj 3414 Pots BFS
  13. 【Uly】关于团队工作流程的反思
  14. 加快pip下载的速度---镜像
  15. 基于Python的人脸识别
  16. 将自家的位置标注到地图上(51ditu.com)
  17. 发布功能完成02《ivx低代码签到系统制作》
  18. word怎么自动换行
  19. reghdfe:多维面板固定效应估计
  20. ebs 供应商地点信息_EBS R12使用接口表往已存在的供应商地址下创建新的地点

热门文章

  1. C++与QML交互(信号与槽通知QML,C++注册QML,QML结点映射C++类)
  2. Qt工作笔记-QString中arg的使用(可用于SQL语句拼接)
  3. cmd编译整个java项目_cmd中使用javac对整个包编译怎么办?对包里面的某几个java文件编译怎么办?...
  4. centos8 忘记root密码
  5. php集成环境还需要mysql吗_是选择php集成环境好还是分开安装的原生版好
  6. 前后端分离html ssm,前后端分离请求ssm返回数据
  7. Linux内核 获取本机mac,Linux获取本机MAC地址
  8. 数据结构之树的存储结构
  9. LeetCode:2. 两数相加(中等)
  10. 计算机网络之传输层:7、TCP拥塞控制