1# 消息队列<a name="ZH-CN_TOPIC_0000001078912736"></a> 2 3- [基本概念](#section81171363232) 4- [运行机制](#section1074515132316) 5 - [队列控制块](#section194431851201315) 6 - [队列运作原理](#section89875741418) 7 8- [开发指导](#section827981242419) 9 - [接口说明](#section19327151642413) 10 - [开发流程](#section1390154210243) 11 12- [编程实例](#section27132341285) 13 - [实例描述](#section197311443141017) 14 - [编程示例](#section972214490107) 15 - [结果验证](#section19287165416106) 16 17 18## 基本概念<a name="section81171363232"></a> 19 20队列又称消息队列,是一种常用于任务间通信的数据结构。队列接收来自任务或中断的不固定长度消息,并根据不同的接口确定传递的消息是否存放在队列空间中。 21 22任务能够从队列里面读取消息,当队列中的消息为空时,挂起读取任务;当队列中有新消息时,挂起的读取任务被唤醒并处理新消息。任务也能够往队列里写入消息,当队列已经写满消息时,挂起写入任务;当队列中有空闲消息节点时,挂起的写入任务被唤醒并写入消息。 23 24可以通过调整读队列和写队列的超时时间来调整读写接口的阻塞模式,如果将读队列和写队列的超时时间设置为0,就不会挂起任务,接口会直接返回,这就是非阻塞模式。反之,如果将都队列和写队列的超时时间设置为大于0的时间,就会以阻塞模式运行。 25 26消息队列提供了异步处理机制,允许将一个消息放入队列,但不立即处理。同时队列还有缓冲消息的作用,可以使用队列实现任务异步通信,队列具有如下特性: 27 28- 消息以先进先出的方式排队,支持异步读写。 29- 读队列和写队列都支持超时机制。 30- 每读取一条消息,就会将该消息节点设置为空闲。 31- 发送消息类型由通信双方约定,可以允许不同长度(不超过队列的消息节点大小)的消息。 32- 一个任务能够从任意一个消息队列接收和发送消息。 33- 多个任务能够从同一个消息队列接收和发送消息。 34- 创建队列时所需的队列空间,接口内系统自行动态申请内存。 35 36## 运行机制<a name="section1074515132316"></a> 37 38### 队列控制块<a name="section194431851201315"></a> 39 40``` 41/** 42 * 队列控制块数据结构 43 */ 44typedef struct { 45 UINT8 *queueHandle; /**< Pointer to a queue handle */ 46 UINT16 queueState; /**< Queue state */ 47 UINT16 queueLen; /**< Queue length */ 48 UINT16 queueSize; /**< Node size */ 49 UINT32 queueID; /**< queueID */ 50 UINT16 queueHead; /**< Node head */ 51 UINT16 queueTail; /**< Node tail */ 52 UINT16 readWriteableCnt[OS_QUEUE_N_RW]; /**< Count of readable or writable resources, 0:readable, 1:writable */ 53 LOS_DL_LIST readWriteList[OS_QUEUE_N_RW]; /**< the linked list to be read or written, 0:readlist, 1:writelist */ 54 LOS_DL_LIST memList; /**< Pointer to the memory linked list */ 55} LosQueueCB; 56``` 57 58每个队列控制块中都含有队列状态,表示该队列的使用情况: 59 60- OS\_QUEUE\_UNUSED:队列未被使用。 61- OS\_QUEUE\_INUSED:队列被使用中。 62 63### 队列运作原理<a name="section89875741418"></a> 64 65- 创建队列时,创建队列成功会返回队列ID。 66- 在队列控制块中维护着一个消息头节点位置Head和一个消息尾节点位置Tail,用于表示当前队列中消息的存储情况。Head表示队列中被占用的消息节点的起始位置。Tail表示被占用的消息节点的结束位置,也是空闲消息节点的起始位置。队列刚创建时,Head和Tail均指向队列起始位置。 67- 写队列时,根据readWriteableCnt\[1\]判断队列是否可以写入,不能对已满(readWriteableCnt\[1\]为0)队列进行写操作。写队列支持两种写入方式:向队列尾节点写入,也可以向队列头节点写入。尾节点写入时,根据Tail找到起始空闲消息节点作为数据写入对象,如果Tail已经指向队列尾部则采用回卷方式。头节点写入时,将Head的前一个节点作为数据写入对象,如果Head指向队列起始位置则采用回卷方式。 68- 读队列时,根据readWriteableCnt\[0\]判断队列是否有消息需要读取,对全部空闲(readWriteableCnt\[0\]为0)队列进行读操作会引起任务挂起。如果队列可以读取消息,则根据Head找到最先写入队列的消息节点进行读取。如果Head已经指向队列尾部则采用回卷方式。 69- 删除队列时,根据队列ID找到对应队列,把队列状态置为未使用,把队列控制块置为初始状态,并释放队列所占内存。 70 71**图 1** 队列读写数据操作示意图<a name="fig139854471119"></a> 72 73 74上图对读写队列做了示意,图中只画了尾节点写入方式,没有画头节点写入,但是两者是类似的。 75 76## 开发指导<a name="section827981242419"></a> 77 78### 接口说明<a name="section19327151642413"></a> 79 80<a name="table10903105695114"></a> 81<table><thead align="left"><tr id="row1293645645110"><th class="cellrowborder" valign="top" width="23.56%" id="mcps1.1.4.1.1"><p id="p59361562512"><a name="p59361562512"></a><a name="p59361562512"></a>功能分类</p> 82</th> 83<th class="cellrowborder" valign="top" width="24.29%" id="mcps1.1.4.1.2"><p id="p1393665645118"><a name="p1393665645118"></a><a name="p1393665645118"></a>接口<strong id="b197068338312"><a name="b197068338312"></a><a name="b197068338312"></a>名称</strong></p> 84</th> 85<th class="cellrowborder" valign="top" width="52.15%" id="mcps1.1.4.1.3"><p id="p119363564516"><a name="p119363564516"></a><a name="p119363564516"></a>描述</p> 86</th> 87</tr> 88</thead> 89<tbody><tr id="row1693665613516"><td class="cellrowborder" rowspan="2" valign="top" width="23.56%" headers="mcps1.1.4.1.1 "><p id="p193675615514"><a name="p193675615514"></a><a name="p193675615514"></a>创建/删除消息队列</p> 90</td> 91<td class="cellrowborder" valign="top" width="24.29%" headers="mcps1.1.4.1.2 "><p id="p11936115612514"><a name="p11936115612514"></a><a name="p11936115612514"></a>LOS_QueueCreate</p> 92</td> 93<td class="cellrowborder" valign="top" width="52.15%" headers="mcps1.1.4.1.3 "><p id="p1593620562517"><a name="p1593620562517"></a><a name="p1593620562517"></a>创建一个消息队列,由系统动态申请队列空间</p> 94</td> 95</tr> 96<tr id="row79361156175113"><td class="cellrowborder" valign="top" headers="mcps1.1.4.1.1 "><p id="p893615567517"><a name="p893615567517"></a><a name="p893615567517"></a>LOS_QueueDelete</p> 97</td> 98<td class="cellrowborder" valign="top" headers="mcps1.1.4.1.2 "><p id="p4936155695111"><a name="p4936155695111"></a><a name="p4936155695111"></a>根据队列ID删除一个指定队列</p> 99</td> 100</tr> 101<tr id="row093614566519"><td class="cellrowborder" rowspan="3" valign="top" width="23.56%" headers="mcps1.1.4.1.1 "><p id="p1593685614513"><a name="p1593685614513"></a><a name="p1593685614513"></a>读/写队列(不带拷贝)</p> 102</td> 103<td class="cellrowborder" valign="top" width="24.29%" headers="mcps1.1.4.1.2 "><p id="p6936556155118"><a name="p6936556155118"></a><a name="p6936556155118"></a>LOS_QueueRead</p> 104</td> 105<td class="cellrowborder" valign="top" width="52.15%" headers="mcps1.1.4.1.3 "><p id="p11936556155118"><a name="p11936556155118"></a><a name="p11936556155118"></a>读取指定队列头节点中的数据(队列节点中的数据实际上是一个地址)</p> 106</td> 107</tr> 108<tr id="row199369565518"><td class="cellrowborder" valign="top" headers="mcps1.1.4.1.1 "><p id="p393655620513"><a name="p393655620513"></a><a name="p393655620513"></a>LOS_QueueWrite</p> 109</td> 110<td class="cellrowborder" valign="top" headers="mcps1.1.4.1.2 "><p id="p12936256175120"><a name="p12936256175120"></a><a name="p12936256175120"></a>向指定队列尾节点中写入入参bufferAddr的值(即buffer的地址)</p> 111</td> 112</tr> 113<tr id="row1293615635114"><td class="cellrowborder" valign="top" headers="mcps1.1.4.1.1 "><p id="p893625665119"><a name="p893625665119"></a><a name="p893625665119"></a>LOS_QueueWriteHead</p> 114</td> 115<td class="cellrowborder" valign="top" headers="mcps1.1.4.1.2 "><p id="p193620566515"><a name="p193620566515"></a><a name="p193620566515"></a>向指定队列头节点中写入入参bufferAddr的值(即buffer的地址)</p> 116</td> 117</tr> 118<tr id="row593675635117"><td class="cellrowborder" rowspan="3" valign="top" width="23.56%" headers="mcps1.1.4.1.1 "><p id="p293675615111"><a name="p293675615111"></a><a name="p293675615111"></a>读/写队列(带拷贝)</p> 119</td> 120<td class="cellrowborder" valign="top" width="24.29%" headers="mcps1.1.4.1.2 "><p id="p14936356155113"><a name="p14936356155113"></a><a name="p14936356155113"></a>LOS_QueueReadCopy</p> 121</td> 122<td class="cellrowborder" valign="top" width="52.15%" headers="mcps1.1.4.1.3 "><p id="p11936155616510"><a name="p11936155616510"></a><a name="p11936155616510"></a>读取指定队列头节点中的数据</p> 123</td> 124</tr> 125<tr id="row093619569510"><td class="cellrowborder" valign="top" headers="mcps1.1.4.1.1 "><p id="p179361256175117"><a name="p179361256175117"></a><a name="p179361256175117"></a>LOS_QueueWriteCopy</p> 126</td> 127<td class="cellrowborder" valign="top" headers="mcps1.1.4.1.2 "><p id="p6936155616515"><a name="p6936155616515"></a><a name="p6936155616515"></a>向指定队列尾节点中写入入参bufferAddr中保存的数据</p> 128</td> 129</tr> 130<tr id="row16936856185111"><td class="cellrowborder" valign="top" headers="mcps1.1.4.1.1 "><p id="p49361156195113"><a name="p49361156195113"></a><a name="p49361156195113"></a>LOS_QueueWriteHeadCopy</p> 131</td> 132<td class="cellrowborder" valign="top" headers="mcps1.1.4.1.2 "><p id="p1193625675116"><a name="p1193625675116"></a><a name="p1193625675116"></a>向指定队列头节点中<em id="i1183903561620"><a name="i1183903561620"></a><a name="i1183903561620"></a>写入入参</em>bufferAddr中保存的数据</p> 133</td> 134</tr> 135<tr id="row1936756155114"><td class="cellrowborder" valign="top" width="23.56%" headers="mcps1.1.4.1.1 "><p id="p149371956105114"><a name="p149371956105114"></a><a name="p149371956105114"></a>获取队列信息</p> 136</td> 137<td class="cellrowborder" valign="top" width="24.29%" headers="mcps1.1.4.1.2 "><p id="p7937145613516"><a name="p7937145613516"></a><a name="p7937145613516"></a>LOS_QueueInfoGet</p> 138</td> 139<td class="cellrowborder" valign="top" width="52.15%" headers="mcps1.1.4.1.3 "><p id="p19371356175110"><a name="p19371356175110"></a><a name="p19371356175110"></a>获取指定队列的信息,包括队列ID、队列长度、消息节点大小、头节点、尾节点、可读节点数量、可写节点数量、等待读操作的任务、等待写操作的任务</p> 140</td> 141</tr> 142</tbody> 143</table> 144 145### 开发流程<a name="section1390154210243"></a> 146 1471. 用LOS\_QueueCreate创建队列。创建成功后,可以得到队列ID。 1482. 通过LOS\_QueueWrite或者LOS\_QueueWriteCopy写队列。 1493. 通过LOS\_QueueRead或者LOS\_QueueReadCopy读队列。 1504. 通过LOS\_QueueInfoGet获取队列信息。 1515. 通过LOS\_QueueDelete删除队列。 152 153> **说明:** 154>- 系统支持的最大队列数是指:整个系统的队列资源总个数,而非用户能使用的个数。例如:系统软件定时器多占用一个队列资源,那么用户能使用的队列资源就会减少一个。 155>- 创建队列时传入的队列名和flags暂时未使用,作为以后的预留参数。 156>- 队列接口函数中的入参timeOut是相对时间。 157>- LOS\_QueueReadCopy和LOS\_QueueWriteCopy及LOS\_QueueWriteHeadCopy是一组接口,LOS\_QueueRead和LOS\_QueueWrite及LOS\_QueueWriteHead是一组接口,每组接口需要配套使用。 158>- 鉴于LOS\_QueueWrite和LOS\_QueueWriteHead和LOS\_QueueRead这组接口实际操作的是数据地址,用户必须保证调用LOS\_QueueRead获取到的指针所指向的内存区域在读队列期间没有被异常修改或释放,否则可能导致不可预知的后果。 159>- 鉴于LOS\_QueueWrite和LOS\_QueueWriteHead和LOS\_QueueRead这组接口实际操作的是数据地址,也就意味着实际写和读的消息长度仅仅是一个指针数据,因此用户使用这组接口之前,需确保创建队列时的消息节点大小,为一个指针的长度,避免不必要的浪费和读取失败。 160 161## 编程实例<a name="section27132341285"></a> 162 163### 实例描述<a name="section197311443141017"></a> 164 165创建一个队列,两个任务。任务1调用写队列接口发送消息,任务2通过读队列接口接收消息。 166 1671. 通过LOS\_TaskCreate创建任务1和任务2。 1682. 通过LOS\_QueueCreate创建一个消息队列。 1693. 在任务1 SendEntry中发送消息。 1704. 在任务2 RecvEntry中接收消息。 1715. 通过LOS\_QueueDelete删除队列。 172 173### 编程示例<a name="section972214490107"></a> 174 175示例代码如下: 176 177``` 178#include "los_task.h" 179#include "los_queue.h" 180static UINT32 g_queue; 181#define BUFFER_LEN 50 182 183VOID SendEntry(VOID) 184{ 185 UINT32 ret = 0; 186 CHAR abuf[] = "test message"; 187 UINT32 len = sizeof(abuf); 188 189 ret = LOS_QueueWriteCopy(g_queue, abuf, len, 0); 190 if(ret != LOS_OK) { 191 printf("send message failure, error: %x\n", ret); 192 } 193} 194 195VOID RecvEntry(VOID) 196{ 197 UINT32 ret = 0; 198 CHAR readBuf[BUFFER_LEN] = {0}; 199 UINT32 readLen = BUFFER_LEN; 200 201 //休眠1s 202 usleep(1000000); 203 ret = LOS_QueueReadCopy(g_queue, readBuf, &readLen, 0); 204 if(ret != LOS_OK) { 205 printf("recv message failure, error: %x\n", ret); 206 } 207 208 printf("recv message: %s\n", readBuf); 209 210 ret = LOS_QueueDelete(g_queue); 211 if(ret != LOS_OK) { 212 printf("delete the queue failure, error: %x\n", ret); 213 } 214 215 printf("delete the queue success!\n"); 216} 217 218UINT32 ExampleQueue(VOID) 219{ 220 printf("start queue example\n"); 221 UINT32 ret = 0; 222 UINT32 task1, task2; 223 TSK_INIT_PARAM_S initParam = {0}; 224 225 initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)SendEntry; 226 initParam.usTaskPrio = 9; 227 initParam.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE; 228 initParam.pcName = "SendQueue"; 229 230 LOS_TaskLock(); 231 ret = LOS_TaskCreate(&task1, &initParam); 232 if(ret != LOS_OK) { 233 printf("create task1 failed, error: %x\n", ret); 234 return ret; 235 } 236 237 initParam.pcName = "RecvQueue"; 238 initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)RecvEntry; 239 ret = LOS_TaskCreate(&task2, &initParam); 240 if(ret != LOS_OK) { 241 printf("create task2 failed, error: %x\n", ret); 242 return ret; 243 } 244 245 ret = LOS_QueueCreate("queue", 5, &g_queue, 0, 50); 246 if(ret != LOS_OK) { 247 printf("create queue failure, error: %x\n", ret); 248 } 249 250 printf("create the queue success!\n"); 251 LOS_TaskUnlock(); 252 return ret; 253} 254``` 255 256### 结果验证<a name="section19287165416106"></a> 257 258编译运行得到的结果为: 259 260``` 261start test example 262create the queue success! 263recv message: test message 264delete the queue success! 265``` 266 267