1 说明
消息队列是实现简单消息队列的内核对象,允许线程和ISR异步发送和接收固定大小的数据项。
2 概念
可以定义任何数量的消息队列。 每个消息队列都由其内存地址引用。
消息队列具有以下关键属性:
已发送但尚未收到的数据项的环形缓冲区。
数据项大小,以字节为单位。
可以在环形缓冲区中排队的最大数量的数据项。
消息队列的环形缓冲区必须与N字节边界对齐,其中N是2的幂(即1,2,4,8,…)。为确保存储在环形缓冲区中的消息与此边界相似,数据项大小必须也是N的倍数。
消息队列在被使用之前必须被初始化。这将其环形缓冲区设置为空。
数据项可以由线程或ISR发送到消息队列。发送线程指向的数据项被复制到一个等待线程(如果存在的话);否则如果空间可用,则将项目复制到消息队列的环形缓冲区中。在任何一种情况下,发送的数据区域的大小必须等于消息队列的数据项大小。
如果线程在环形缓冲区已满时尝试发送数据项,发送线程可能会选择等待空间变为可用。当环形缓冲区已满时,任意数量的发送线程可能会同时等待;当空间变得可用时,它被赋予等待时间最长的最高优先级发送线程。
数据项可以由线程从消息队列接收。数据项被复制到接收线程指定的区域;接收区域的大小必须等于消息队列的数据项大小。
如果线程尝试在环形缓冲区为空时接收数据项,接收线程可能会选择等待发送数据项。当环形缓冲区为空时,任意数量的接收线程可能会同时等待;当数据项变得可用时,它被赋予等待时间最长的最高优先级接收线程。
注意:内核确实允许ISR从消息队列接收项目,但是如果消息队列为空,则ISR不能尝试等待。
3 操作
3.1 定义一个消息队列
消息队列使用 struct k_msgq 类型的变量来定义。它必须通过调用k_msgq_init() 来初始化。
以下代码定义并初始化一个空的消息队列,该消息队列能够保存10个项目,每个项目的长度为12个字节。
struct data_item_type {
u32_t field1;
u32_t field2;
u32_t field3;
};
char __aligned(4) my_msgq_buffer[10 * sizeof(struct data_item_type)];
struct k_msgq my_msgq;
k_msgq_init(&my_msgq, my_msgq_buffer, sizeof(struct data_item_type), 10);
或者,可以通过调用 K_MSGQ_DEFINE 在编译时定义和初始化消息队列。
以下代码与上面的代码段具有相同的效果。观察宏定义了消息队列及其缓冲区。
K_MSGQ_DEFINE(my_msgq, sizeof(data_item_type), 10, 4);
3.2 写入消息队列
通过调用 k_msgq_put() 将数据项添加到消息队列中。
以下代码构建在上述示例上,并使用消息队列将数据项从生成线程传递到一个或多个消费线程。 如果消息队列由于消费者无法跟上而填满,则生成线程会抛弃所有现有数据,以便保存新数据。
void producer_thread(void)
{
struct data_item_type data;
int i = 0;
while (1) {
/* create data item to send (e.g. measurement, timestamp, ...) */
data.field1 = i++;
/* send data to consumers */
while (k_msgq_put(&my_msgq, &data, K_NO_WAIT) != 0) {
/* message queue is full: purge old data & try again */
k_msgq_purge(&my_msgq);
}
/* data item was successfully added to message queue */
}
}
3.3 从消息队列中读取
通过调用 k_msgq_get() 从数据队列中获取数据项。
以下代码构建在上述示例上,并使用消息队列处理由一个或多个生成线程生成的数据项。
void consumer_thread(void)
{
struct data_item_type data;
while (1) {
/* get a data item */
k_msgq_get(&my_msgq, &data, K_FOREVER);
/* process data item */
...
}
}
4 建议用法
使用消息队列以异步方式在线程之间传输小数据项。
如果需要,可以使用消息队列来传输大数据项。但是,这会增加中断延迟,因为在写入或读取数据项时中断被锁定。 通常通过交换指向数据项的指针而不是数据项本身来传送大数据项。内核的内存映射和内存池对象类型可以有助于此类数据传输。
通过使用内核的邮箱对象类型可以实现同步传输。
5 配置选项
无
6 APIs
下列消息队列API,都在 kernel.h 中提供了:
K_MSGQ_DEFINE
k_msgq_init()
k_msgq_put()
k_msgq_get()
k_msgq_purge()
k_msgq_num_used_get()
k_msgq_num_free_get()