1. 概述

队列又称消息队列,是一种常用于任务间通信的数据结构,实现了接收来自任务或中断的不固定长度的消息,并根据不同的接口选择传递消息是否存放在自己空间。任务能够从队列里面读取消息,当队列中的消息是空时,挂起读取任务;当队列中有新消息时,挂起的读取任务被唤醒并处理新消息。

用户在处理业务时,消息队列提供了异步处理机制,允许将一个消息放入队列,但并不立即处理它,同时队列还能起到缓冲消息作用。

Huawei LiteOS中使用队列数据结构实现任务异步通信工作,具有如下特性:

  • 消息以先进先出方式排队,支持异步读写工作方式。
  • 读队列和写队列都支持超时机制。
  • 发送消息类型由通信双方约定,可以允许不同长度(不超过队列节点最大值)消

    息。
  • 一个任务能够从任意一个消息队列接收和发送消息。
  • 多个任务能够从同一个消息队列接收和发送消息。
  • 当队列使用结束后,如果是动态申请的内存,需要通过释放内存函数回收。

2. 运作机制

2.1 队列控制块

/**
* @ingroup los_queue
* Queue information block structure
*/
typedef struct tagQueueCB
{
UINT8 *pucQueue; /**< 队列指针 */
UINT16 usQueueState; /**< 队列状态 */
UINT16 usQueueLen; /**< 队列中消息个数 */
UINT16 usQueueSize; /**< 消息节点大小 */
UINT16 usQueueHead; /**< 消息头节点位置(数组下标)*/
UINT16 usQueueTail; /**< 消息尾节点位置(数组下标)*/
UINT16 usWritableCnt; /**< 队列中可写消息数*/
UINT16 usReadableCnt; /**< 队列中可读消息数*/
UINT16 usReserved; /**< 保留字段 */
LOS_DL_LIST stWriteList; /**< 写入消息任务等待链表*/
LOS_DL_LIST stReadList; /**< 读取消息任务等待链表*/
LOS_DL_LIST stMemList; /**< MailBox模块使用 */
} QUEUE_CB_S;

每个队列控制块中都含有队列状态,表示该队列的使用情况:

  • OS_QUEUE_UNUSED:队列没有使用
  • OS_QUEUE_INUSED:队列被使用

2.2 队列运作原理

创建队列时,根据用户传入队列长度和消息节点大小来开辟相应的内存空间以供该队列使用,返回队列ID。

在队列控制块中维护一个消息头节点位置Head和一个消息尾节点位置Tail来表示当前队列中消息存储情况。 Head表示队列中被占用消息的起始位置。 Tail表示队列中被空闲消息的起始位置。刚创建时Head和Tail均指向队列起始位置。

写队列时,根据Tail找到被占用消息节点末尾的空闲节点作为数据写入对象。如果Tail已经指向队列尾则采用回卷方式。根据usWritableCnt判断队列是否可以写入,不能对已满(usWritableCnt为0)队列进行写队列操作。

读队列时,根据Head找到最先写入队列中的消息节点进行读取。如果Head已经指向队如果Head已经指向队列尾则采用回卷方式。根据usReadableCnt判断队列是否有消息读取,对全部空闲(usReadableCnt为0)队列进行读队列操作会引起任务挂起。

删除队列时,根据传入的队列ID寻找到对应的队列,把队列状态置为未使用,释放原队列所占的空间,对应的队列控制头置为初始状态。

图 3-7 队列读写数据操作示意图

3. 开发指导

3.1 功能

Huawei LiteOS中Message消息处理模块提供了以下功能。

功能分类 接口名 描述
创建消息队列 LOS_QueueCreate 创建一个消息队列。
读队列(不带拷贝) LOS_QueueRead 读取指定队列中的数据。(buff里存放的是队列节点的地址)。
写队列(不带拷贝) LOS_QueueWrite 向指定队列写数据。(写入队列节点中的是buff的地址)。
读队列(带拷贝) LOS_QueueReadCopy 读取指定队列中的数据。(buff里存放的是队列节点中的数据)。
写队列(带拷贝) LOS_QueueWriteCopy 向指定队列写数据。(写入队列节点中的是buff中的数据)。
写队列(头部) LOS_QueueWriteHead 向指定队列的头部写数据
删除队列 LOS_QueueDelete 删除一个指定的队列。
获取队列信息 LOS_QueueInfoGet 获取指定队列信息。

3.2 开发流程

使用队列模块的典型流程如下:

  1. 创建消息队列LOS_QueueCreate。

创建成功后,可以得到消息队列的ID值。

  1. 写队列操作函数LOS_QueueWrite。
  2. 读队列操作函数LOS_QueueRead。
  3. 获取队列信息函数LOS_QueueInfoGet。
  4. 删除队列LOS_QueueDelete

3.3 QUEUE 错误码

对队列存在失败可能性的操作,包括创建队列、删除队列等等,均需要返回对应的错误码,以便快速定位错误原因。

序 号 定义 实际数值 描述 参考解决方案
1 LOS_ERRNO_QUEUE_MAXNUM_ZERO 0x02000600 队列资源的最大数目配置为0 配置要大于0的队列资源的最大数量。如果不使用队列模块,则将配置项设置为将队列资源的最大数量的剪裁设置为NO。
2 LOS_ERRNO_QUEUE_NO_MEMORY 0x02000601 队列块内存无法初始化 为队列块分配更大的内存分区,或减少队列资源的最大数量。
3 LOS_ERRNO_QUEUE_CREATE_NO_MEMORY 0x02000602 队列创建的内存未能被请求 为队列分配更多的内存,或减少要创建的队列中的队列长度和节点的数目。
4 LOS_ERRNO_QUEUE_SIZE_TOO_BIG 0x02000603 队列创建时 消息长度超过上限更改创建队列中最大消息的大小至不超过上线
5 LOS_ERRNO_QUEUE_CB_UNAVAILABLE 0x02000604 已超过创建的队列的数量的上限 增加队列的配置资源数量
6 LOS_ERRNO_QUEUE_NOT_FOUND 0x02000605 无效的队列 确保队列ID是有效的
7 LOS_ERRNO_QUEUE_PEND_IN_LOCK 0x02000606 当任务被锁定时,禁止在队列中被阻塞 使用队列前解锁任务
8 LOS_ERRNO_QUEUE_TIMEOUT 0x02000607 等待处理队列的时间超时 检查设置的超时时间是否合适
9 LOS_ERRNO_QUEUE_IN_TSKUSE 0x02000608 阻塞任务的队列不能被删除 使任务能够获得资源而不是在队列中被阻塞
10 LOS_ERRNO_QUEUE_WRITE_IN_INTERRUPT 0x02000609 在中断处理程序中不能写队列 将写队列设为非阻塞模式
11 LOS_ERRNO_QUEUE_NOT_CREATE 0x0200060a 队列未创建 检查队列中传递的句柄是否有效
12 LOS_ERRNO_QUEUE_IN_TSKWRITE 0x0200060b 队列读写不同步 同步队列的读写
13 LOS_ERRNO_QUEUE_CREAT_PTR_NULL 0x0200060c 队列创建过程中传递的参数为空指针 确保传递的参数不为空指针
14 LOS_ERRNO_QUEUE_PARA_ISZERO 0x0200060d 队列创建过程中传递的队列长度或消息节点大小为0 传入正确的队列长度和消息节点大小
15 LOS_ERRNO_QUEUE_READ_INVALID 0x0200060e 读取的队列的handle无效 检查队列中传递的handle是否有效
16 LOS_ERRNO_QUEUE_READ_PTR_NULL 0x0200060f 队列读取过程中传递的指针为空 检查指针中传递的是否为空
17 LOS_ERRNO_QUEUE_READSIZE_ISZERO 0x02000610 队列读取过程中传递的缓冲区大小为0 通过一个正确的缓冲区大小
18 LOS_ERRNO_QUEUE_WRITE_INVALID 0x02000611 队列写入过程中传递的队列handle无效 检查队列中传递的handle是否有效
19 LOS_ERRNO_QUEUE_WRITE_PTR_NULL 0x02000612 队列写入过程中传递的指针为空 检查指针中传递的是否为空
20 LOS_ERRNO_QUEUE_WRITESIZE_ISZERO 0x02000613 队列写入过程中传递的缓冲区大小为0 通过一个正确的缓冲区大小
21 LOS_ERRNO_QUEUE_WRITE_NOT_CREATE 0x02000614 写入数据的队列未创建 传入有效队列ID
22 LOS_ERRNO_QUEUE_WRITE_SIZE_TOO_BIG 0x02000615 队列写入过程中传递的缓冲区大小比队列大小要大 减少缓冲区大小,或增大队列节点
23 LOS_ERRNO_QUEUE_ISFULL 0x02000616 在队列写入过程中没有可用的空闲节点 确保在队列写入之前,可以使用空闲的节点
24 LOS_ERRNO_QUEUE_PTR_NULL 0x02000617 正在获取队列信息时传递的指针为空 检查指针中传递的是否为空
25 LOS_ERRNO_QUEUE_READ_IN_INTERRUPT 0x02000618 在中断处理程序中不能读队列 将读队列设为非阻塞模式
26 LOS_ERRNO_QUEUE_MAIL_HANDLE_INVALID 0x02000619 正在释放队列的内存时传递的队列的handle无效 检查队列中传递的handle是否有效
27 LOS_ERRNO_QUEUE_MAIL_PTR_INVALID 0x0200061a 传入的消息内存池指针为空 检查指针是否为空
28 LOS_ERRNO_QUEUE_MAIL_FREE_ERROR 0x0200061b membox内存释放失败 传入非空membox内存指针
29 LOS_ERRNO_QUEUE_READ_NOT_CREATE 0x0200061c 待读取的队列未创建 传入有效队列ID
30 LOS_ERRNO_QUEUE_ISEMPTY 0x0200061d 队列已空 确保在读取队列时包含消息
31 LOS_ERRNO_QUEUE_READ_SIZE_TOO_SMALL 0x0200061f 读缓冲区大小小于队列大小 增加缓冲区大小,或减小队列节点大小

3.4 注意事项

  • 系统可配置的队列资源个数是指:整个系统的队列资源总个数,而非用户能使用的个数。例如:系统软件定时器多占用一个队列资源,那么系统可配置的队列资源就会减少一个。
  • 调用 LOS_QueueCreate 函数时所传入的队列名暂时未使用,作为以后的预留参

    数。
  • 队列接口函数中的入参uwTimeOut是指相对时间。
  • LOS_QueueReadCopy和LOS_QueueWriteCopy是一组接口, LOS_QueueRead和LOS_QueueWrite是一组接口,两组接口需要配套使用。
  • 鉴于LOS_QueueWrite和LOS_QueueRead这组接口实际操作的是数据地址,用户必须保证调用LOS_QueueRead获取到的指针所指向内存区域在读队列期间没有被异常修改或释放,否则可能会导致不可预知的后果。

3.5 编程实例

3.5.1 实例描述

创建一个队列,两个任务。任务1调用发送接口发送消息;任务2通过接收接口接收消息。

  1. 通过LOS_TaskCreate创建任务1和任务2。
  2. 通过LOS_QueueCreate创建一个消息队列。
  3. 在任务1 send_Entry中发送消息。
  4. 在任务2 recv_Entry中接收消息。
  5. 通过LOS_QueueDelete删除队列。

3.5.2 编程示例

#include "los_task.h"
#include "los_queue.h"
static UINT32 g_uwQueue;
CHAR abuf[] = "test is message x";
/*任务1发送数据*/
void *send_Entry(void *arg)
{
UINT32 i = 0,uwRet = 0;
UINT32 uwlen = sizeof(abuf);
while (i <5)
{
abuf[uwlen -2] = '0' + i;
i++;
/*将abuf里的数据写入队列*/
uwRet = LOS_QueueWrite(g_uwQueue, abuf, uwlen, 0);
if(uwRet != LOS_OK)
{
dprintf("send message failure,error:%x\n",uwRet);
}
LOS_TaskDelay(5);
}
}
/*任务2接收数据*/
void *recv_Entry(void *arg)
{
UINT32 uwReadbuf;
UINT32 uwRet = 0;
while (1)
{
/*读取队列里的数据存入uwReadbuf里*/
uwRet = LOS_QueueRead(g_uwQueue, &uwReadbuf, 50, 0);
if(uwRet != LOS_OK)
{
dprintf("recv message failure,error:%x\n",uwRet);
break;
}
dprintf("recv message:%s\n", (char *)uwReadbuf);
LOS_TaskDelay(5);
}
/*删除队列*/
while (LOS_OK != LOS_QueueDelete(g_uwQueue))
{
LOS_TaskDelay(1);
}
dprintf("delete the queue success!\n");
}
int Example_creat_task(void)
{
UINT32 uwRet = 0;
UINT32 uwTask1, uwTask2;
TSK_INIT_PARAM_S stInitParam1;
/*创建任务1*/
stInitParam1.pfnTaskEntry = send_Entry;
stInitParam1.usTaskPrio = 9;
stInitParam1.uwStackSize = 0x400;
stInitParam1.pcName = "sendQueue";
stInitParam1.uwResved = LOS_TASK_STATUS_DETACHED;
LOS_TaskLock();//锁住任务,防止新创建的任务比本任务高而发生调度
uwRet = LOS_TaskCreate(&uwTask1, &stInitParam1);
if(uwRet != LOS_OK)
{
dprintf("create task1 failed!,error:%x\n",uwRet);
return uwRet;
}
/*创建任务2*/
stInitParam1.pfnTaskEntry = recv_Entry;
uwRet = LOS_TaskCreate(&uwTask2, &stInitParam1);
if(uwRet != LOS_OK)
{
dprintf("create task2 failed!,error:%x\n",uwRet);
return uwRet;
}
/*创建队列*/
uwRet = LOS_QueueCreate("queue", 5, &g_uwQueue, 0, 50);
if(uwRet != LOS_OK)
{
dprintf("create queue failure!,error:%x\n",uwRet);
}
dprintf("create the queue success!\n");
LOS_TaskUnlock();//解锁任务,只有队列创建后才开始任务调度
}

3.5.3 结果验证

liteos队列(五)的更多相关文章

  1. SDUT-3332&3333_数据结构实验之栈与队列五:下一较大值

    数据结构实验之栈与队列六:下一较大值 Time Limit: 150 ms Memory Limit: 8000 KiB Problem Description 对于包含n(1<=n<=1 ...

  2. 数据结构实验之栈与队列五:下一较大值(一)(SDUT 3332)

    #include <bits/stdc++.h> using namespace std; int a[1005]; int main() { int t,n,i,j; while(~sc ...

  3. 【LiteOS】LiteOS消息队列

    目录 前言 链接 参考 笔录草稿 基本概念 队列运作机制 队列运作原理 消息队列传输方式 消息队列的阻塞机制 出队阻塞 入队阻塞 任务相关函数 任务开发流程 注意事项 * 实战 前言 链接 LiteO ...

  4. 【转】IPC-消息队列

    一.    概念 消息队列就是一个消息的链表.对消息队列有写权限的进程可以向其中按照一定的规则添加新消息:对消息队列有读权限的进程可以从消息队列中读出消息.消息队列是随内核持续的.下面介绍三个概念: ...

  5. IPC之消息队列详解与使用

    一.    概念 消息队列就是一个消息的链表.对消息队列有写权限的进程可以向其中按照一定的规则添加新消息:对消息队列有读权限的进程可以从消息队列中读出消息.消息队列是随内核持续的.下面介绍三个概念: ...

  6. django-celery 创建多个broker队列 异步执行任务时指定队列

    一.这里不再详细述说 django 框架中如何使用celery, 重点放在如何实现创建多个队列, 并指定队列存放异步任务 笔者使用   django-celery==3.2.2 模块, 配置项及配置参 ...

  7. C#消息队列(RabbitMQ)零基础从入门到实战演练

    一.课程介绍 如果您从工作中之听过但未有接触过消息对队列(MQ),如果你接触过一点关于MQ的知识,如果没有这么的多如果的话......,那么阿笨将通过本次<C#消息队列零基础从入门到实战演练&g ...

  8. MQ中将消息发送至远程队列的配置

    MQ中将消息发送至远程队列的配置 摘自MQ资源管理器帮助文档V7 在开始学习本教程之前,您需要从系统管理员处了解标识网络上接收机器的名称:IP地址.MQ的端口号.队列管理器.接收(远程机器)或者是发送 ...

  9. 【windows 操作系统】进程间通信(IPC)简述|无名管道和命名管道 消息队列、信号量、共享存储、Socket、Streams等

    一.进程间通信简述 每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进 ...

随机推荐

  1. k8s资产清单(二)

    什么是清单 说白了清单是k8s当中用来定义pod的文件,语法格式遵循yaml语法,在yaml当中可以定义控制器类型,元数据,容器端口号等等等....,也可以针对于清单对pod进行删除等操作 为什么太学 ...

  2. win10安装并激活office

    安装并且激活office 相信很多小伙伴在平时安装office时都会和一些流氓软件做过激烈的斗争,但是还是没有安装成功, 最后完美的安装了一些流氓软件.(苦笑) 下面我们来安装office并且破解 下 ...

  3. unittest单元测试,基于java的junit测试框架

    import unittestclass study(unittest.TestCase): def testXia(self): self.assertEqual((3*4),20) def tes ...

  4. 关于destoon6.0下的ngnix伪静态

    关于destoon6.0下的ngnix伪静态配置 ##rewrite nginx rewrite '(.*)\.(asp|aspx|asa|asax|dll|jsp|cgi|fcgi|pl)(.*)' ...

  5. day52_9_16Django中的静态文件和orm

    一.静态文件配置 在配置静态文件时,需要创建一个文件夹在Django项目文件夹下,名字与使用无关. 静态文件包括html等使用的不会变动的插件文件等.分为三个部分: css文件夹 当前网站所有的样式文 ...

  6. git 入门教程之 git bash 竟然不支持 tree 命令

    开门见山 git bash 是 Windows 用户安装 git 时默认安装的命令行工具,不仅界面漂亮功能也不错,大多数情况下可以替代 Windows 原生的 cmd 命令行. 然而,git bash ...

  7. 安装Microsoft Office 2010 提示需要安装MSXML版本6.10.1129.0 的解决方案

    1.先按照https://blog.csdn.net/c_lanxiaofang/article/details/80410868进行处理安装 2.安装vison2010,如果还存在提示未安装MSXM ...

  8. Redis的Sorted-Sets排行榜功能实现

    Redis的ZSet排行榜功能实现 1. 功能需求 类似给用户n张图片, 用户左滑不喜欢右滑喜欢.所以每个用户就会有一些喜欢的图片集合和不喜欢的图片集合.现在我们要做一个将按照一个算法将喜欢的排到前面 ...

  9. ASP.NET MVC https全局配置

    //https全局配置 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; ServicePointManager.S ...

  10. Tensorflow源码解析2 -- 前后端连接的桥梁 - Session

    Session概述 1. Session是TensorFlow前后端连接的桥梁.用户利用session使得client能够与master的执行引擎建立连接,并通过session.run()来触发一次计 ...