天天看点

zephyr 消息队列

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()
           

继续阅读