一、机制简介
Zephyr实现消息队列的功能,允许线程和ISR异步地接收和发送固定大小的消息数据项。如果有等待消息的线程存在,那么就会把消息数据项直接拷贝给这个线程,特别声明的是这里只做数据项的内容的拷贝复制,并没有把消息数据项的地址传递。另外有一种情况是如果ring buffer有空间,那么这个消息数据项会被拷贝到消息队列的ring buffer。还有一点就是,发送数据的大小都必须和一开始定义的消息队列的数据项大小一致。
如果发送线程向一个已满的消息队列的ring buffer发送数据,那么线程将被阻塞,直到ring buffer有空闲的存储空间。这个动作可以任意多个线程同时执行。也就是多个线程可以同时向这个已满的队列中写数据,但是都会被阻塞。当队列有空间时,那个优先级最高等待时间最长的队列,可以被唤醒,然后向队列中写数据。
一个消息数据项可以被线程接收。这个数据项被拷贝到线程指定的内存地址(就是前面提到的,是传值,而不是址)。这个指定的接收区域的空间,必须等于数据项大小。接收时也是类似,如果队列是空,也就是没有数据项时,那么这个接收线程,将被阻塞。任意多个线程可以同时接收同一个消息,没有消息时都被阻塞,当有消息时优先级最高等待时间最长的线程将被唤醒接收数据。
收受线程还可以查看消息队列中的head消息,换句话说,就是读取消息队列的数据项之后,不把这个数据项从队列中删除。
二、相关API及实现
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(data_item_type)];
struct k_msgq my_msgq;
k_msgq_init(&my_msgq, my_msgq_buffer, sizeof(data_item_type), 10);
或者,使用K_MSGQ_DEFINE在编译时定义定初始化。和上面的代码效果相同:
K_MSGQ_DEFINE(my_msgq, sizeof(data_item_type), 10, 4);
2.写数据到消息队列
可以使用k_msgq_put()添加一个数据项到消息队列。
下面的示例代码,从一个生产者传输数据到多个消费者。如果数据没有被消费者及时取出,那么生产者会清空队列,继续存入新数据。
void producer_thread(void)
{
struct data_item_t data;
while (1) {
/* create data item to send (e.g. measurement, timestamp, ...) */
data = ...
/* 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.从消息队列读一个数项
可以使用k_msgq_get()从队列中读取一个数据项。
作为消费者去读取数据项:
void consumer_thread(void)
{
struct data_item_t data;
while (1) {
/* get a data item */
k_msgq_get(&my_msgq, &data, K_FOREVER);
/* process data item */
...
}
}
4.查看消息队列的头数项
可以使用k_msgq_peek()函数去看看一个数据项。
下面的示例代码,查看head数据项:
void consumer_thread(void)
{
struct data_item_t data;
while (1) {
/* read a data item by peeking into the queue */
k_msgq_peek(&my_msgq, &data);
/* process data item */
...
}
}
(PS:上面的示例代码参考网上,只做学习使用,zephyr官方代码的使用不如网上的这段示例代码逻辑简单明显故放网上示例代码)