🚀 优质资源分享 🚀
学习路线指引(点击解锁) | 知识定位 | 人群定位 |
---|---|---|
🧡 Python实战微信订餐小程序 🧡 | 进阶级 | 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。 |
💛Python量化交易实战💛 | 入门级 | 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统 |
目录* 前言
- 10.1 消息队列概念
- 10.2 消息队列的数据传输机制
- 10.3 消息队列的阻塞访问机制
- 10.4 消息队列使用场景
- 10.5 消息队列控制块
- 10.6 创建消息队列
- 10.7 发送消息
- 10.8 接收消息
- 10.9 窥探消息
- 10.10 队列查询
- 10.11 删除消息队列
- 10.12 消息队列使用注意
- 小结
前言
消息队列是任务间通信系列介绍的首篇笔记,因为学习完消息队列的源码实现后,信号量、互斥量这些任务间通信机制也相当于学完了,只剩下概念性的内容了。
参考:
10.1 消息队列概念
消息队列实任务间通信机制中的一种。
其它还有二值信号量、计数信号量、互斥量和递归互斥量等等。
一个或多个任务往一个消息容器里面发消息,其它一个或多个任务从这个消息容器里面获取消息,这样实现通信。
/* 该图源自野火 */
freertos的消息队列:
- 支持FIFO、支持LIFO也支持异步读写工作方式。
- 支持超时机制。
- 支持不同长度(在节点长度范围内)、任意类型的消息。
- 一个任务可对一个消息队列读、写。
- 一个消息队列支持被多个任务读、写。
- 队列使用一次后自动从消息队列中移除。
10.2 消息队列的数据传输机制
队列传输数据有两种方式:
- 拷贝:把数据、把变量的值复制进队列里。
- 引用:把数据、把变量的地址复制进队列里。
而freertos的消息队列机制就是拷贝,拷贝的方式有以下优点:
- 局部变量的值可以发送到队列中,后续即使函数退出、局部变量被回收,也不会影响队列中的数据。
- 无需分配buffer来保存数据,队列中有buffer。
- 发送任务、接收任务解耦:接收任务不需要知道这数据是谁的、也不需要发送任务来释放数据。
- 如果数据实在太大,可以选择传输地址(即是拷贝地址),依然能实现传输引用的效果。
- 队列的空间有FreeRTOS内核分配,无需上层应用维护。
- 无需考虑内存保护功能,因为拷贝的方式新数据的存储区是由队列组件提供的,无需担心获取消息的任务需要权限访问。
当然对比引用的方式也有劣势:
- 拷贝数据相对拷贝引用来说要耗时。
- 需要更多内存,因为需要存储数据副本。
10.3 消息队列的阻塞访问机制
只要拿到队列句柄,任务和中断都有权限访问消息队列,但是也有阻塞限制。
写消息时,如果消息队列已满,则无法写入(覆盖写入除外),如果用户设置的阻塞时间不为0,则任务会进入阻塞,直到该队列有空闲空间给当前任务写入消息或阻塞时间超时才解除阻塞。
上面说的“该队列有空闲空间给当前任务写入消息”是因为就算当前队列有空间空间,也会优先安排阻塞在等待写链表中的最高优先级任务先写入。如果任务优先级相同,则先安排给最早开始等待的那个任务先写。
读消息时,机制和写消息一样,只是阻塞的条件是队列里面没有消息。
数据传输和阻塞访问机制都会在分析源码时阐述。
10.4 消息队列使用场景
消息队列可以应用于发送不定长消息的场合,包括任务与任务间的消息交换。
队列是FreeRTOS主要的任务间通讯方式,可以在任务与任务间、中断和任务间传送信息。
发送到队列的消息是通过拷贝方式实现的,这意味着队列存储的数据是原数据,而不是原数据的引用。
10.5 消息队列控制块
消息队列的控制块也是队列控制块,这个控制块的数据结构除了被消息队列使用,还被使用到二值信号量、计数信号量、互斥量和递归互斥量。
FreeRTOS的消息队列控制块由多个元素组成,当消息队列被创建时,系统会为控制块分配对应的内存空间,用于保存消息队列的一些信息,包括数据区位置、队列状态等等。
10.5.1 队列控制块源码
队列控制块struct QueueDefinition
源码:
/*
* Definition of the queue used by the scheduler.
* Items are queued by copy, not reference. See the following link for the
* rationale: http://www.freertos.org/Embedded-RTOS-Queues.html
*/
typedef struct QueueDefinition
{
int8\_t *pcHead; /*< Points to the beginning of the queue storage area. */
int8\_t *pcTail; /*< Points to the byte at the end of the queue storage area. Once more byte is allocated than necessary to store the queue items, this is used as a marker. */
int8\_t *pcWriteTo; /*< Points to the free next place in the storage area. */
union /* Use of a union is an exception to the coding standard to ensure two mutually exclusive structure members don't appear simultaneously (wasting RAM). */
{
int8\_t *pcReadFrom; /*< Points to the last place that a queued item was read from when the structure is used as a queue. */
UBaseType\_t uxRecursiveCallCount;/*< Maintains a count of the number of times a recursive mutex has been recursively 'taken' when the structure is used as a mutex. */
} u;
List\_t xTasksWaitingToSend; /*< List of tasks that are blocked waiting to post onto this queue. Stored in priority order. */
List\_t xTasksWaitingToReceive; /*< List of tasks that are blocked waiting to read from this queue. Stored in priority order. */
volatile UBaseType\_t uxMessagesWaiting;/*< The number of items currently in the queue. */
UBaseType\_t uxLength; /*< The length of the queue defined as the number of items it will hold, not the number of bytes. */
UBaseType\_t uxItemSize; /*< The size of each items that the queue will hold. */
volatile int8\_t cRxLock; /*< Stores the number of items received from the queue (removed from the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */
volatile int8\_t cTxLock; /*< Stores the number of items transmitted to the queue (added to the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */
#if( ( configSUPPORT\_STATIC\_ALLOCATION == 1 ) && ( configSUPPORT\_DYNAMIC\_ALLOCATION == 1 ) )
uint8\_t ucStaticallyAllocated; /*< Set to pdTRUE if the memory used by the queue was statically allocated to ensure no attempt is made to free the memory. */
#endif
#if ( configUSE\_QUEUE\_SETS == 1 )
struct QueueDefinition *pxQueueSetContainer;
#endif
#if ( configUSE\_TRACE\_FACILITY == 1 )
UBaseType\_t uxQueueNumber;
uint8\_t ucQueueType;
#endif
} xQUEUE;
10.5.2 队列控制块成员剖析
在成员剖析时默认按消息队列的作用去剖析。
int8_t *pcHead;
:
/* 该队列存储区的起始位置,对应第一个消息空间。*/
int8\_t *pcHead;
int8_t *pcTail;
:
/* 消息队列存储区的结尾位置。
* 结合 pcHead 指针就是整个存储区合法区域。*/
int8\_t *pcHead;
int8_t *pcWriteTo;
:
/* 写指针,指向存储区中下一个空闲的空间。
* 队列下次写数据的位置,需要入队时调用该指针写入数据。*/
int8\_t *pcWriteTo;
int8_t *pcReadFrom;
:
/* 读指针,指向存储区中下一个有效数据的空间。
* 队列下次读取数据的位置,需要出队时调用该指针写入数据。*/
int8\_t *pcReadFrom;
UBaseType_t uxRecursiveCallCount;
:
/* 递归次数。
* 用于互斥量时使用,与 pcReadFrom 为联合体。
* 记录递归互斥量被调用的次数。 */