数据队列
简介
数据队列是另外一种任务间的通信机制,在发送数据时,需要指定数据的地址和大小。它能够完成邮箱和消息队列所能够实现的功能,但又有一些差异性。具体差异如下:
(1)邮箱在发送数据时,发送固定大小的数据(4byte);而数据队列传送的是固定大小的数据项(即struct os_data_item,该数据项包含数据地址和大小);
(2)消息队列初始化时,指定了每条消息的最大长度,发送消息时复制该消息内容到消息队列预先分配好的缓冲区中,即消息队列传送的是具体消息内容,而数据队列传送的是固定大小的数据项;
(3)数据队列增加了一个通知回调函数;
(4)数据队列增加了一个接口,可以读取但保留数据队列中的数据;
(5)数据队列增加了一个门限值,决定在何种情况下读取数据时唤醒被阻塞的任务。
重要定义及数据结构
宏定义
#define OS_DATAQUEUE_EVENT_UNKNOWN 0x00
#define OS_DATAQUEUE_EVENT_POP 0x01
#define OS_DATAQUEUE_EVENT_PUSH 0x02
#define OS_DATAQUEUE_EVENT_THRESHOLD 0x03
定义 | 说明 |
---|---|
OS_DATAQUEUE_EVENT_UNKNOWN | 未知的事件 |
OS_DATAQUEUE_EVENT_POP | POP数据时,若剩余的数据项个数大于门限值,则调用通知回调函数并通知该事件 |
OS_DATAQUEUE_EVENT_PUSH | PUSH数据时,若没有POP任务阻塞,则会调用通知回调函数并通知该事件;否则唤醒被阻塞的POP任务 |
OS_DATAQUEUE_EVENT_THRESHOLD | POP数据时,若剩余的数据项个数小于等于门限值,则调用通知回调函数并通知该事件 |
结构体
struct os_data_item
{
const void *data_ptr;
os_size_t data_size;
};
struct os_data_item参数 | 说明 |
---|---|
data_ptr | 发送数据的地址 |
data_size | 发送数据的大小 |
struct os_data_queue
{
os_uint16_t size; /* Data queue holds the maximum number of data item. */
os_uint16_t threshold; /* When the number of data item less than this,resume the push task. */
os_uint16_t get_index; /* get index */
os_uint16_t put_index; /* put index */
os_data_item_t *queue; /* The pointer of data item. */
os_list_node_t suspended_push_list; /* push task on this list. */
os_list_node_t suspended_pop_list; /* pop task on this list. */
void (*evt_notify)(struct os_data_queue *queue, os_uint32_t event); /* event notify */
};
struct os_data_queue | 说明 |
---|---|
size | 数据队列的大小 |
threshold | 门限值,当队列中的数据项少于等于此门限时,读取数据会唤醒被阻塞的任务 |
get_index | 数据项读取索引 |
put_index | 数据项写入索引 |
queue | 数据项队列头 |
suspended_push_list | 阻塞在该数据队列上的发送任务 |
suspended_pop_list | 阻塞在该数据队列上的接收任务 |
evt_notify | 通知回调函数;在push时,若没有pop任务阻塞,该函数会被调用;在pop时被调用 |
API列表
接口 | 说明 |
---|---|
os_data_queue_init | 初始化数据队列 |
os_data_queue_deinit | 反初始化数据队列 |
os_data_queue_push | 向指定的数据队列中发送数据,当数据队列已满,则在指定的超时时间内挂起等待 |
os_data_queue_pop | 从指定的数据队列中接收数据,如果数据队列为空,则在指定的时间内挂起等待 |
os_data_queue_peak | 从指定的数据队列中读取但保留数据队列中的数据 |
os_data_queue_reset | 恢复数据队列上挂起等待的所有任务 |
os_data_queue_init
该函数用于初始化数据队列,可以设定通知回调函数,函数原型如下:
os_err_t os_data_queue_init(os_data_queue_t *queue,
os_uint16_t size,
os_uint16_t threshold,
void (*evt_notify)(os_data_queue_t *queue, os_uint32_t event));
参数 | 说明 |
---|---|
queue | 数据队列句柄 |
size | 数据队列的大小 |
threshold | 门限值,当队列中的数据项少于等于此门限时,读取队列会唤醒被阻塞的任务 |
evt_notify | 通知回调函数,在push或pop时被调用 |
返回 | 说明 |
OS_EOK | 返回成功 |
OS_ENOMEM | 失败,没有空间 |
os_data_queue_deinit
该函数用于反初始化数据队列,函数原型如下:
os_err_t os_data_queue_deinit(os_data_queue_t *queue);
参数 | 说明 |
---|---|
queue | 数据队列句柄 |
返回 | 说明 |
OS_EOK | 成功 |
os_data_queue_push
该函数用于向指定的数据队列中发送数据,若数据队列满且需要等待,则当前任务阻塞。PUSH结束时若有POP任务阻塞,会唤醒POP任务;PUSH结束时若没有POP任务阻塞,则通知OS_DATAQUEUE_EVENT_PUSH。函数原型如下:
os_err_t os_data_queue_push(os_data_queue_t *queue, const void *data_ptr, os_size_t data_size, os_tick_t timeout);
参数 | 说明 |
---|---|
queue | 数据队列句柄 |
data_ptr | 发送数据的起始地址 |
data_size | 发送数据的大小 |
timeout | 数据队列满时的超时等待时间;若为OS_IPC_WAITING_NO,则直接返回OS_ETIMEOUT;若为OS_IPC_WAITING_FOREVER,则永久等待直到数据队列有空余可用;若为其它值,则等待timeout时间或者直到数据队列有空余可用 |
返回 | 说明 |
OS_EOK | 发送成功 |
OS_ETIMEOUT | 发送超时 |
os_data_queue_pop
该函数用于从指定的数据队列中接收数据,若数据队列为空且需要等待,则当前任务阻塞。POP结束时若剩余数据项个数小于等于门限值,则(有PUSH任务阻塞时)唤醒被阻塞的PUSH任务,通知OS_DATAQUEUE_EVENT_THRESHOLD;POP结束时若剩余数据项大于门限值,则通知OS_DATAQUEUE_EVENT_POP。函数原型如下:
os_err_t os_data_queue_pop(os_data_queue_t *queue, const void** data_ptr, os_size_t *size, os_tick_t timeout);
参数 | 说明 |
---|---|
queue | 数据队列句柄 |
data_ptr | 指向接收数据的地址 |
size | 接收数据的大小 |
timeout | 当数据队列为空时的超时等待时间;若为OS_IPC_WAITING_NO,则直接返回OS_ETIMEOUT;若为OS_IPC_WAITING_FOREVER,则永久等待直到数据队列有数据;若为其它值,则等待timeout时间或者直到数据队列有数据 |
返回 | 说明 |
OS_EOK | 接收成功 |
OS_ETIMEOUT | 接收超时 |
os_data_queue_peak
该函数用于从指定的数据队列中读取数据,但仍然保留数据队列中的该数据项,若数据队列为空则不等待直接退出,函数原型如下:
os_err_t os_data_queue_peak(os_data_queue_t *queue, const void** data_ptr, os_size_t *size);
参数 | 说明 |
---|---|
queue | 数据队列句柄 |
data_ptr | 指向接收数据的地址 |
size | 接收数据的大小 |
返回 | 说明 |
OS_EOK | 接收成功 |
OS_EEMPTY | 数据队列为空 |
os_data_queue_reset
该函数恢复数据队列上挂起等待的所有任务,函数原型如下:
void os_data_queue_reset(os_data_queue_t *queue);
参数 | 说明 |
---|---|
queue | 数据队列句柄 |
返回 | 说明 |
无 | 无 |
使用示例
数据队列使用示例
本例初始化数据队列后,创建了一个任务用于发送数据,另外一个任务用于接收数据,接收数据时采用了pop和peak两种方式,可以看到通过peak读取一个数据项后,该数据项仍然保留在数据队列中,通过pop读取该数据项后,无法再获得该数据项
#include <oneos_config.h>
#include <os_dbg.h>
#include <os_errno.h>
#include <os_task.h>
#include <shell.h>
#include <string.h>
#include <os_ipc.h>
#include <os_dataqueue.h>
#define TEST_TAG "TEST"
#define TASK_STACK_SIZE 1024
#define TASK1_PRIORITY 15
#define TASK2_PRIORITY 16
#define TASK_TIMESLICE 10
#define STR_NUM 4
char *str[STR_NUM] = {
"hello, world",
"it's a new day",
"it's a nice day",
"it's a wonderful day"
};
static os_data_queue_t dataqueue_test;
void test_dataqueue_notify(os_data_queue_t *queue, os_uint32_t event)
{
LOG_W(TEST_TAG, "dataqueue notify:%d",event);
}
void task1_entry(void *para)
{
os_uint32_t i = 0;
const void *data_ptr = OS_NULL;
os_size_t data_size = 0;
while(1)
{
if (i < (STR_NUM/2))
{
if (OS_EOK == os_data_queue_peak(&dataqueue_test, &data_ptr, &data_size))
{
LOG_W(TEST_TAG, "task1 dataqueue peak ptr:%p size:%d str:%s", data_ptr, data_size, data_ptr);
}
else
{
LOG_W(TEST_TAG, "task1 dataqueue peak: empty");
}
}
if (OS_EOK == os_data_queue_pop(&dataqueue_test, &data_ptr, &data_size, OS_IPC_WAITING_FOREVER))
{
LOG_W(TEST_TAG, "task1 dataqueue pop ptr :%p size:%d str:%s", data_ptr, data_size, data_ptr);
}
i++;
os_task_sleep(200);
}
}
void task2_entry(void *para)
{
os_uint32_t i = 0;
const void *data_ptr = OS_NULL;
os_size_t data_size = 0;
for (i = 0; i < STR_NUM; i++) {
data_ptr = str[i];
data_size = strlen(str[i]) + 1;
LOG_W(TEST_TAG, "task2 dataqueue push ptr:%p size:%d str:%s", data_ptr, data_size, data_ptr);
if (OS_EOK != os_data_queue_push(&dataqueue_test, data_ptr, data_size, OS_IPC_WAITING_FOREVER))
{
LOG_W(TEST_TAG, "task2 dataqueue push ERR");
}
os_task_sleep(100);
}
}
void dataqueue_sample(void)
{
os_task_t *task1 = OS_NULL;
os_task_t *task2 = OS_NULL;
os_data_queue_init(&dataqueue_test, 10, 10, test_dataqueue_notify);
task1 = os_task_create("task1",
task1_entry,
OS_NULL,
TASK_STACK_SIZE,
TASK1_PRIORITY,
TASK_TIMESLICE);
if (task1)
{
LOG_W(TEST_TAG, "dataqueue_sample startup task1");
os_task_startup(task1);
}
task2 = os_task_create("task2",
task2_entry,
OS_NULL,
TASK_STACK_SIZE,
TASK2_PRIORITY,
TASK_TIMESLICE);
if (task2)
{
LOG_W(TEST_TAG, "dataqueue_sample startup task2");
os_task_startup(task2);
}
}
SH_CMD_EXPORT(test_dataqueue, dataqueue_sample, "test dataqueue");
运行结果如下:
sh />test_dataqueue
W/TEST: dataqueue_sample startup task1
W/TEST: task1 dataqueue peak: empty
W/TEST: dataqueue_sample startup task2
W/TEST: task2 dataqueue push ptr:08027564 size:13 str:hello, world
W/TEST: dataqueue notify:3
W/TEST: task1 dataqueue pop ptr :08027564 size:13 str:hello, world
W/TEST: task2 dataqueue push ptr:0802759c size:15 str:it's a new day
W/TEST: dataqueue notify:2
W/TEST: task1 dataqueue peak ptr:0802759c size:15 str:it's a new day
W/TEST: dataqueue notify:3
W/TEST: task1 dataqueue pop ptr :0802759c size:15 str:it's a new day
W/TEST: task2 dataqueue push ptr:08027574 size:16 str:it's a nice day
W/TEST: dataqueue notify:2
W/TEST: task2 dataqueue push ptr:08027584 size:21 str:it's a wonderful day
W/TEST: dataqueue notify:2
W/TEST: dataqueue notify:3
W/TEST: task1 dataqueue pop ptr :08027574 size:16 str:it's a nice day
W/TEST: dataqueue notify:3
W/TEST: task1 dataqueue pop ptr :08027584 size:21 str:it's a wonderful day