4.4 队列

    基本概念

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

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

    • 消息以先进先出方式排队,支持异步读写工作方式。

    • 读队列和写队列都支持超时机制。

    • 发送消息类型由通信双方约定,可以允许不同长度(不超过队列节点最大值)消息。

    • 一个任务能够从任意一个消息队列接收和发送消息。

    • 多个任务能够从同一个消息队列接收和发送消息。

    • 当队列使用结束后,如果是动态申请的内存,需要通过释放内存函数回收。

    运作机制

    队列控制块

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

    • OS_QUEUE_UNUSED:队列没有使用

    • OS_QUEUE_INUSED:队列被使用

    队列运作原理

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

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

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

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

    队列读写数据操作示意图

    功能

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

    开发流程

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

    1. 创建消息队列LOS_QueueCreate。

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

    2. 写队列操作函数LOS_QueueWrite。

    3. 读队列操作函数LOS_QueueRead。

    4. 获取队列信息函数LOS_QueueInfoGet。

    5. 删除队列LOS_QueueDelete。

    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_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_PTR_NULL 0x02000612 队列写入过程中传递的指针为空 检查指针中传递的是否为空
    19 LOS_ERRNO_QUEUE_WRITESIZE_ISZERO 0x02000613 队列写入过程中传递的缓冲区大小为0 通过一个正确的缓冲区大小
    20 LOS_ERRNO_QUEUE_WRITE_SIZE_TOO_BIG 0x02000615 队列写入过程中传递的缓冲区大小比队列大小要大 减少缓冲区大小,或增大队列节点
    21 LOS_ERRNO_QUEUE_ISFULL 0x02000616 在队列写入过程中没有可用的空闲节点 确保在队列写入之前,可以使用空闲的节点
    22 LOS_ERRNO_QUEUE_PTR_NULL 0x02000617 正在获取队列信息时传递的指针为空 检查指针中传递的是否为空
    23 LOS_ERRNO_QUEUE_READ_IN_INTERRUPT 0x02000618 在中断处理程序中不能读队列 将读队列设为非阻塞模式
    24 LOS_ERRNO_QUEUE_MAIL_HANDLE_INVALID 0x02000619 正在释放队列的内存时传递的队列的handle无效 检查队列中传递的handle是否有效
    25 LOS_ERRNO_QUEUE_MAIL_PTR_INVALID 0x0200061a 传入的消息内存池指针为空 检查指针是否为空
    26 LOS_ERRNO_QUEUE_MAIL_FREE_ERROR 0x0200061b membox内存释放失败 传入非空membox内存指针
    27 LOS_ERRNO_QUEUE_ISEMPTY 0x0200061d 队列已空 确保在读取队列时包含消息
    28 LOS_ERRNO_QUEUE_READ_SIZE_TOO_SMALL 0x0200061f 读缓冲区大小小于队列大小 增加缓冲区大小,或减小队列节点大小

    平台差异性

    无。

    • 系统可配置的队列资源个数是指:整个系统的队列资源总个数,而非用户能使用的个数。例如:系统软件定时器多占用一个队列资源,那么系统可配置的队列资源就会减少一个。

    • 调用LOS_QueueCreate 函数时所传入的队列名暂时未使用,作为以后的预留参数。

    • 队列接口函数中的入参uwTimeOut是指相对时间。

    • LOS_QueueReadCopy和LOS_QueueWriteCopy是一组接口,LOS_QueueRead和LOS_QueueWrite是一组接口,两组接口需要配套使用。

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

    实例描述

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

    1. 通过LOS_TaskCreate创建任务1和任务2。

    2. 通过LOS_QueueCreate创建一个消息队列。

    3. 在任务1 send_Entry中发送消息。

    4. 在任务2 recv_Entry中接收消息。

    5. 通过LOS_QueueDelete删除队列。

    编程示例

    1. #include "los_task.h"
    2. #include "los_swtmr.h"
    3. #include "los_hwi.h"
    4. #include "los_queue.h"
    5. #include "los_event.h"
    6. #include "los_typedef.h"
    7. #include "los_api_msgqueue.h"
    8. #include "los_inspect_entry.h"
    9. #ifdef __cplusplus
    10. #if __cplusplus
    11. extern "C" {
    12. #endif /* __cpluscplus */
    13. #endif /* __cpluscplus */
    14. static UINT32 g_uwQueue;
    15. static CHAR abuf[] = "test is message x";
    16. /*任务1发送数据*/
    17. static void *send_Entry(UINT32 uwParam1,
    18. UINT32 uwParam2,
    19. UINT32 uwParam3,
    20. {
    21. UINT32 i = 0,uwRet = 0;
    22. UINT32 uwlen = sizeof(abuf);
    23. while (i < API_MSG_NUM)
    24. {
    25. abuf[uwlen -2] = '0' + i;
    26. i++;
    27. /*将abuf里的数据写入队列*/
    28. uwRet = LOS_QueueWrite(g_uwQueue, abuf, uwlen, 0);
    29. if(uwRet != LOS_OK)
    30. {
    31. dprintf("send message failure,error:%x\n",uwRet);
    32. }
    33. LOS_TaskDelay(5);
    34. }
    35. return NULL;
    36. }
    37. /*任务2接收数据*/
    38. static void *recv_Entry(UINT32 uwParam1,
    39. UINT32 uwParam3,
    40. UINT32 uwParam4)
    41. {
    42. UINT32 uwReadbuf;
    43. UINT32 uwRet = LOS_OK;
    44. UINT32 uwMsgCount = 0;
    45. while (1)
    46. {
    47. /*读取队列里的数据存入uwReadbuf里*/
    48. uwRet = LOS_QueueRead(g_uwQueue, &uwReadbuf, 24, 0);
    49. if(uwRet != LOS_OK)
    50. {
    51. dprintf("recv message failure,error:%x\n",uwRet);
    52. break;
    53. }
    54. else
    55. {
    56. dprintf("recv message:%s\n", (char *)uwReadbuf);
    57. uwMsgCount++;
    58. }
    59. (void)LOS_TaskDelay(5);
    60. }
    61. /*删除队列*/
    62. while (LOS_OK != LOS_QueueDelete(g_uwQueue))
    63. {
    64. (void)LOS_TaskDelay(1);
    65. }
    66. dprintf("delete the queue success!\n");
    67. if(API_MSG_NUM == uwMsgCount)
    68. uwRet = LOS_InspectStatusSetByID(LOS_INSPECT_MSG,LOS_INSPECT_STU_SUCCESS);
    69. if (LOS_OK != uwRet)
    70. {
    71. dprintf("Set Inspect Status Err\n");
    72. }
    73. }
    74. else
    75. {
    76. uwRet = LOS_InspectStatusSetByID(LOS_INSPECT_MSG,LOS_INSPECT_STU_ERROR);
    77. if (LOS_OK != uwRet)
    78. {
    79. }
    80. }
    81. return NULL;
    82. }
    83. UINT32 Example_MsgQueue(void)
    84. {
    85. UINT32 uwRet = 0;
    86. UINT32 uwTask1, uwTask2;
    87. TSK_INIT_PARAM_S stInitParam1;
    88. /*创建任务1*/
    89. stInitParam1.pfnTaskEntry = send_Entry;
    90. stInitParam1.usTaskPrio = 9;
    91. stInitParam1.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
    92. stInitParam1.pcName = "sendQueue";
    93. LOS_TaskLock();//锁住任务,防止新创建的任务比本任务高而发生调度
    94. uwRet = LOS_TaskCreate(&uwTask1, &stInitParam1);
    95. if(uwRet != LOS_OK)
    96. {
    97. dprintf("create task1 failed!,error:%x\n",uwRet);
    98. return uwRet;
    99. }
    100. /*创建任务2*/
    101. stInitParam1.pfnTaskEntry = recv_Entry;
    102. uwRet = LOS_TaskCreate(&uwTask2, &stInitParam1);
    103. if(uwRet != LOS_OK)
    104. {
    105. dprintf("create task2 failed!,error:%x\n",uwRet);
    106. return uwRet;
    107. }
    108. /*创建队列*/
    109. uwRet = LOS_QueueCreate("queue", 5, &g_uwQueue, 0, 24);
    110. if(uwRet != LOS_OK)
    111. {
    112. dprintf("create queue failure!,error:%x\n",uwRet);
    113. }
    114. dprintf("create the queue success!\n");
    115. LOS_TaskUnlock();//解锁任务,只有队列创建后才开始任务调度
    116. return LOS_OK;
    117. }

    结果验证
    4.4.队列 - 图2