更新日期: 2022/06/01 来源: https://gitee.com/weharmony/kernel_liteos_a_note
los_queue.c 文件参考

浏览源代码.

函数

LITE_OS_SEC_TEXT_INIT UINT32 OsQueueInit (VOID)
 
LITE_OS_SEC_TEXT_INIT UINT32 LOS_QueueCreate (CHAR *queueName, UINT16 len, UINT32 *queueID, UINT32 flags, UINT16 maxMsgSize)
 创建一个队列,根据用户传入队列长度和消息节点大小来开辟相应的内存空间以供该队列使用,参数queueID带走队列ID 更多...
 
STATIC LITE_OS_SEC_TEXT UINT32 OsQueueReadParameterCheck (UINT32 queueID, const VOID *bufferAddr, const UINT32 *bufferSize, UINT32 timeout)
 读队列参数检查 更多...
 
STATIC LITE_OS_SEC_TEXT UINT32 OsQueueWriteParameterCheck (UINT32 queueID, const VOID *bufferAddr, const UINT32 *bufferSize, UINT32 timeout)
 写队列参数检查 更多...
 
STATIC VOID OsQueueBufferOperate (LosQueueCB *queueCB, UINT32 operateType, VOID *bufferAddr, UINT32 *bufferSize)
 队列buf操作,注意队列数据是按顺序来读取的,要不从头,要不从尾部,不会出现从中间读写,所有可由 head 和 tail 来管理队列. 更多...
 
STATIC UINT32 OsQueueOperateParamCheck (const LosQueueCB *queueCB, UINT32 queueID, UINT32 operateType, const UINT32 *bufferSize)
 队列操作参数检查 更多...
 
UINT32 OsQueueOperate (UINT32 queueID, UINT32 operateType, VOID *bufferAddr, UINT32 *bufferSize, UINT32 timeout)
 队列操作.是读是写由operateType定 本函数是消息队列最重要的一个函数,可以分析出读取消息过程中 发生的细节,涉及任务的唤醒和阻塞,阻塞链表任务的相互提醒. 更多...
 
LITE_OS_SEC_TEXT UINT32 LOS_QueueReadCopy (UINT32 queueID, VOID *bufferAddr, UINT32 *bufferSize, UINT32 timeout)
 接口函数定时读取消息队列 更多...
 
LITE_OS_SEC_TEXT UINT32 LOS_QueueWriteHeadCopy (UINT32 queueID, VOID *bufferAddr, UINT32 bufferSize, UINT32 timeout)
 接口函数从队列头开始写 更多...
 
LITE_OS_SEC_TEXT UINT32 LOS_QueueWriteCopy (UINT32 queueID, VOID *bufferAddr, UINT32 bufferSize, UINT32 timeout)
 接口函数 从队列尾部开始写 更多...
 
LITE_OS_SEC_TEXT UINT32 LOS_QueueRead (UINT32 queueID, VOID *bufferAddr, UINT32 bufferSize, UINT32 timeout)
 Read a queue. 更多...
 
LITE_OS_SEC_TEXT UINT32 LOS_QueueWrite (UINT32 queueID, VOID *bufferAddr, UINT32 bufferSize, UINT32 timeout)
 Write data into a queue. 更多...
 
LITE_OS_SEC_TEXT UINT32 LOS_QueueWriteHead (UINT32 queueID, VOID *bufferAddr, UINT32 bufferSize, UINT32 timeout)
 Write data into a queue header. 更多...
 
LITE_OS_SEC_TEXT_INIT UINT32 LOS_QueueDelete (UINT32 queueID)
 Delete a queue. 更多...
 
LITE_OS_SEC_TEXT_MINOR UINT32 LOS_QueueInfoGet (UINT32 queueID, QUEUE_INFO_S *queueInfo)
 外部接口, 获取队列信息,用queueInfo 把 LosQueueCB数据接走,QUEUE_INFO_S对内部数据的封装 更多...
 

变量

LITE_OS_SEC_BSS LosQueueCBg_allQueue = NULL
 消息队列池 更多...
 
LITE_OS_SEC_BSS STATIC LOS_DL_LIST g_freeQueueList
 空闲队列链表,管分配的,需要队列从这里申请 更多...
 

详细描述

   基本概念
       队列又称消息队列,是一种常用于任务间通信的数据结构。队列接收来自任务或中断的
       不固定长度消息,并根据不同的接口确定传递的消息是否存放在队列空间中。
       
       任务能够从队列里面读取消息,当队列中的消息为空时,挂起读取任务;当队列中有新消息时,
       挂起的读取任务被唤醒并处理新消息。任务也能够往队列里写入消息,当队列已经写满消息时,
       挂起写入任务;当队列中有空闲消息节点时,挂起的写入任务被唤醒并写入消息。如果将
       读队列和写队列的超时时间设置为0,则不会挂起任务,接口会直接返回,这就是非阻塞模式。

       消息队列提供了异步处理机制,允许将一个消息放入队列,但不立即处理。同时队列还有缓冲消息的作用。
       
   队列特性
       消息以先进先出的方式排队,支持异步读写。
       读队列和写队列都支持超时机制。
       每读取一条消息,就会将该消息节点设置为空闲。
       发送消息类型由通信双方约定,可以允许不同长度(不超过队列的消息节点大小)的消息。
       一个任务能够从任意一个消息队列接收和发送消息。
       多个任务能够从同一个消息队列接收和发送消息。
       创建队列时所需的队列空间,默认支持接口内系统自行动态申请内存的方式,同时也支持将用户分配的队列空间作为接口入参传入的方式。

   队列运作原理
       创建队列时,创建队列成功会返回队列ID。

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

       写队列时,根据readWriteableCnt[1]判断队列是否可以写入,不能对已满(readWriteableCnt[1]为0)
       队列进行写操作。写队列支持两种写入方式:向队列尾节点写入,也可以向队列头节点写入。尾节点写入时,
       根据Tail找到起始空闲消息节点作为数据写入对象,如果Tail已经指向队列尾部则采用回卷方式。头节点写入时,
       将Head的前一个节点作为数据写入对象,如果Head指向队列起始位置则采用回卷方式。

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

       删除队列时,根据队列ID找到对应队列,把队列状态置为未使用,把队列控制块置为初始状态。
       如果是通过系统动态申请内存方式创建的队列,还会释放队列所占内存。

   使用场景
       队列用于任务间通信,可以实现消息的异步处理。同时消息的发送方和接收方不需要彼此联系,两者间是解耦的。

   队列错误码
       对存在失败可能性的操作返回对应的错误码,以便快速定位错误原因。
* 

在文件 los_queue.c 中定义.

函数说明

◆ OsQueueBufferOperate()

STATIC VOID OsQueueBufferOperate ( LosQueueCB queueCB,
UINT32  operateType,
VOID *  bufferAddr,
UINT32 bufferSize 
)

队列buf操作,注意队列数据是按顺序来读取的,要不从头,要不从尾部,不会出现从中间读写,所有可由 head 和 tail 来管理队列.

在文件 los_queue.c245 行定义.

246{
247 UINT8 *queueNode = NULL;
248 UINT32 msgDataSize;
249 UINT16 queuePosition;
250
251 /* get the queue position | 先找到队列的位置*/
252 switch (OS_QUEUE_OPERATE_GET(operateType)) {//获取操作类型
253 case OS_QUEUE_READ_HEAD://从列队头开始读
254 queuePosition = queueCB->queueHead;//拿到头部位置
255 ((queueCB->queueHead + 1) == queueCB->queueLen) ? (queueCB->queueHead = 0) : (queueCB->queueHead++);//调整队列头部位置
256 break;
257 case OS_QUEUE_WRITE_HEAD://从列队头开始写
258 (queueCB->queueHead == 0) ? (queueCB->queueHead = queueCB->queueLen - 1) : (--queueCB->queueHead);//调整队列头部位置
259 queuePosition = queueCB->queueHead;//拿到头部位置
260 break;
261 case OS_QUEUE_WRITE_TAIL://从列队尾部开始写
262 queuePosition = queueCB->queueTail;//设置队列位置为尾部位置
263 ((queueCB->queueTail + 1) == queueCB->queueLen) ? (queueCB->queueTail = 0) : (queueCB->queueTail++);//调整队列尾部位置
264 break;
265 default: /* read tail, reserved. */
266 PRINT_ERR("invalid queue operate type!\n");
267 return;
268 }
269 //queueHandle是create队列时,由外界参数申请的一块内存. 用于copy 使用
270 queueNode = &(queueCB->queueHandle[(queuePosition * (queueCB->queueSize))]);//拿到队列节点
271
272 if (OS_QUEUE_IS_READ(operateType)) {//读操作处理,读队列分两步走
273 if (memcpy_s(&msgDataSize, sizeof(UINT32), queueNode + queueCB->queueSize - sizeof(UINT32),
274 sizeof(UINT32)) != EOK) {//1.先读出队列大小,由队列头四个字节表示
275 PRINT_ERR("get msgdatasize failed\n");
276 return;
277 }
278 msgDataSize = (*bufferSize < msgDataSize) ? *bufferSize : msgDataSize;
279 if (memcpy_s(bufferAddr, *bufferSize, queueNode, msgDataSize) != EOK) {//2.读表示读走已有数据,所以相当于bufferAddr接着了queueNode的数据
280 PRINT_ERR("copy message to buffer failed\n");
281 return;
282 }
283
284 *bufferSize = msgDataSize;//通过入参 带走消息的大小
285 } else {//只有读写两种操作,这里就是写队列了.写也分两步走 , @note_thinking 这里建议鸿蒙加上 OS_QUEUE_IS_WRITE 判断
286 if (memcpy_s(queueNode, queueCB->queueSize, bufferAddr, *bufferSize) != EOK) {//1.写入消息内容
287 PRINT_ERR("store message failed\n");//表示把外面数据写进来,所以相当于queueNode接着了bufferAddr的数据
288 return;
289 }
290 if (memcpy_s(queueNode + queueCB->queueSize - sizeof(UINT32), sizeof(UINT32), bufferSize,
291 sizeof(UINT32)) != EOK) {//2.写入消息数据的长度,sizeof(UINT32)
292 PRINT_ERR("store message size failed\n");
293 return;
294 }
295 }
296}
unsigned short UINT16
Definition: los_typedef.h:56
unsigned char UINT8
Definition: los_typedef.h:55
unsigned int UINT32
Definition: los_typedef.h:57
UINT16 queueHead
Definition: los_queue_pri.h:94
UINT16 queueLen
Definition: los_queue_pri.h:91
UINT16 queueTail
Definition: los_queue_pri.h:95
UINT16 queueSize
Definition: los_queue_pri.h:92
UINT8 * queueHandle
Definition: los_queue_pri.h:89
这是这个函数的调用关系图:

◆ OsQueueInit()

LITE_OS_SEC_TEXT_INIT UINT32 OsQueueInit ( VOID  )

在文件 los_queue.c105 行定义.

106{
107 LosQueueCB *queueNode = NULL;
108 UINT32 index;
109 UINT32 size;
110
111 size = LOSCFG_BASE_IPC_QUEUE_LIMIT * sizeof(LosQueueCB);//支持1024个IPC队列
112 /* system resident memory, don't free */
113 g_allQueue = (LosQueueCB *)LOS_MemAlloc(m_aucSysMem0, size);//常驻内存
114 if (g_allQueue == NULL) {
115 return LOS_ERRNO_QUEUE_NO_MEMORY;
116 }
117 (VOID)memset_s(g_allQueue, size, 0, size);//清0
118 LOS_ListInit(&g_freeQueueList);//初始化空闲链表
119 for (index = 0; index < LOSCFG_BASE_IPC_QUEUE_LIMIT; index++) {//循环初始化每个消息队列
120 queueNode = ((LosQueueCB *)g_allQueue) + index;//一个一个来
121 queueNode->queueID = index;//这可是 队列的身份证
122 LOS_ListTailInsert(&g_freeQueueList, &queueNode->readWriteList[OS_QUEUE_WRITE]);//通过写节点挂到空闲队列链表上
123 }//这里要注意是用 readWriteList 挂到 g_freeQueueList链上的,所以要通过 GET_QUEUE_LIST 来找到 LosQueueCB
124
125 if (OsQueueDbgInitHook() != LOS_OK) {//调试队列使用的.
126 return LOS_ERRNO_QUEUE_NO_MEMORY;
127 }
128 return LOS_OK;
129}
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListInit(LOS_DL_LIST *list)
Definition: los_list.h:104
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListTailInsert(LOS_DL_LIST *list, LOS_DL_LIST *node)
Insert a node to the tail of a doubly linked list.
Definition: los_list.h:244
VOID * LOS_MemAlloc(VOID *pool, UINT32 size)
从指定内存池中申请size长度的内存,注意这可不是从内核堆空间中申请内存
Definition: los_memory.c:1123
UINT8 * m_aucSysMem0
异常交互动态内存池地址的起始地址,当不支持异常交互特性时,m_aucSysMem0等于m_aucSysMem1。
Definition: los_memory.c:107
LITE_OS_SEC_BSS LosQueueCB * g_allQueue
消息队列池
Definition: los_queue.c:98
LITE_OS_SEC_BSS STATIC LOS_DL_LIST g_freeQueueList
空闲队列链表,管分配的,需要队列从这里申请
Definition: los_queue.c:99
STATIC INLINE UINT32 OsQueueDbgInitHook(VOID)
@ OS_QUEUE_WRITE
写队列
Definition: los_queue_pri.h:64
UINT32 queueID
Definition: los_queue_pri.h:93
LOS_DL_LIST readWriteList[OS_QUEUE_N_RW]
Definition: los_queue_pri.h:98
函数调用图:
这是这个函数的调用关系图:

◆ OsQueueOperate()

UINT32 OsQueueOperate ( UINT32  queueID,
UINT32  operateType,
VOID *  bufferAddr,
UINT32 bufferSize,
UINT32  timeout 
)

队列操作.是读是写由operateType定 本函数是消息队列最重要的一个函数,可以分析出读取消息过程中 发生的细节,涉及任务的唤醒和阻塞,阻塞链表任务的相互提醒.

参数
queueID
operateType
bufferAddr
bufferSize
timeout
返回
UINT32

在文件 los_queue.c322 行定义.

323{
324 UINT32 ret;
325 UINT32 readWrite = OS_QUEUE_READ_WRITE_GET(operateType);//获取读/写操作标识
326 UINT32 intSave;
327 OsHookCall(LOS_HOOK_TYPE_QUEUE_READ, (LosQueueCB *)GET_QUEUE_HANDLE(queueID), operateType, *bufferSize, timeout);
328
329 SCHEDULER_LOCK(intSave);
330 LosQueueCB *queueCB = (LosQueueCB *)GET_QUEUE_HANDLE(queueID);//获取对应的队列控制块
331 ret = OsQueueOperateParamCheck(queueCB, queueID, operateType, bufferSize);//参数检查
332 if (ret != LOS_OK) {
333 goto QUEUE_END;
334 }
335
336 if (queueCB->readWriteableCnt[readWrite] == 0) {//根据readWriteableCnt判断队列是否有消息读/写
337 if (timeout == LOS_NO_WAIT) {//不等待直接退出
338 ret = OS_QUEUE_IS_READ(operateType) ? LOS_ERRNO_QUEUE_ISEMPTY : LOS_ERRNO_QUEUE_ISFULL;
339 goto QUEUE_END;
340 }
341
342 if (!OsPreemptableInSched()) {//不支持抢占式调度
343 ret = LOS_ERRNO_QUEUE_PEND_IN_LOCK;
344 goto QUEUE_END;
345 }
346 //任务等待,这里很重要啊,将自己从就绪列表摘除,让出了CPU并发起了调度,并挂在readWriteList[readWrite]上,挂的都等待读/写消息的task
347 LosTaskCB *runTask = OsCurrTaskGet();
348 OsTaskWaitSetPendMask(OS_TASK_WAIT_QUEUE, queueCB->queueID, timeout);
349 ret = runTask->ops->wait(runTask, &queueCB->readWriteList[readWrite], timeout);
350 if (ret == LOS_ERRNO_TSK_TIMEOUT) {//唤醒后如果超时了,返回读/写消息失败
351 ret = LOS_ERRNO_QUEUE_TIMEOUT;
352 goto QUEUE_END;//
353 }
354 } else {
355 queueCB->readWriteableCnt[readWrite]--;//对应队列中计数器--,说明一条消息只能被读/写一次
356 }
357
358 OsQueueBufferOperate(queueCB, operateType, bufferAddr, bufferSize);//发起读或写队列操作
359
360 if (!LOS_ListEmpty(&queueCB->readWriteList[!readWrite])) {//如果还有任务在排着队等待读/写入消息(当时不能读/写的原因有可能当时队列满了==)
361 LosTaskCB *resumedTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&queueCB->readWriteList[!readWrite]));//取出要读/写消息的任务
362 OsTaskWakeClearPendMask(resumedTask);
363 resumedTask->ops->wake(resumedTask);
364 SCHEDULER_UNLOCK(intSave);
365 LOS_MpSchedule(OS_MP_CPU_ALL);//让所有CPU发出调度申请,因为很可能那个要读/写消息的队列是由其他CPU执行
366 LOS_Schedule();//申请调度
367 return LOS_OK;
368 } else {
369 queueCB->readWriteableCnt[!readWrite]++;//对应队列读/写中计数器++
370 }
371
372QUEUE_END:
373 SCHEDULER_UNLOCK(intSave);
374 return ret;
375}
LITE_OS_SEC_ALW_INLINE STATIC INLINE BOOL LOS_ListEmpty(LOS_DL_LIST *list)
Identify whether a specified doubly linked list is empty. | 判断链表是否为空
Definition: los_list.h:321
VOID LOS_Schedule(VOID)
Trigger active task scheduling.
Definition: los_sched.c:469
VOID LOS_MpSchedule(UINT32 target)
Definition: los_mp.c:76
STATIC UINT32 OsQueueOperateParamCheck(const LosQueueCB *queueCB, UINT32 queueID, UINT32 operateType, const UINT32 *bufferSize)
队列操作参数检查
Definition: los_queue.c:298
STATIC VOID OsQueueBufferOperate(LosQueueCB *queueCB, UINT32 operateType, VOID *bufferAddr, UINT32 *bufferSize)
队列buf操作,注意队列数据是按顺序来读取的,要不从头,要不从尾部,不会出现从中间读写,所有可由 head 和 tail 来管理队列.
Definition: los_queue.c:245
STATIC INLINE LosTaskCB * OsCurrTaskGet(VOID)
STATIC INLINE BOOL OsPreemptableInSched(VOID)
STATIC INLINE VOID OsTaskWaitSetPendMask(UINT16 mask, UINTPTR lockID, UINT32 timeout)
设置事件阻塞掩码,即设置任务的等待事件.
Definition: los_task_pri.h:289
STATIC INLINE VOID OsTaskWakeClearPendMask(LosTaskCB *resumeTask)
清除事件阻塞掩码,即任务不再等待任何事件.
Definition: los_task_pri.h:298
UINT16 readWriteableCnt[OS_QUEUE_N_RW]
Definition: los_queue_pri.h:96
UINT32(* wait)(LosTaskCB *runTask, LOS_DL_LIST *list, UINT32 timeout)
任务等待
VOID(* wake)(LosTaskCB *taskCB)
任务唤醒
const SchedOps * ops
函数调用图:
这是这个函数的调用关系图:

◆ OsQueueOperateParamCheck()

STATIC UINT32 OsQueueOperateParamCheck ( const LosQueueCB queueCB,
UINT32  queueID,
UINT32  operateType,
const UINT32 bufferSize 
)

队列操作参数检查

在文件 los_queue.c298 行定义.

300{
301 if ((queueCB->queueID != queueID) || (queueCB->queueState == OS_QUEUE_UNUSED)) {//队列ID和状态判断
302 return LOS_ERRNO_QUEUE_NOT_CREATE;
303 }
304
305 if (OS_QUEUE_IS_WRITE(operateType) && (*bufferSize > (queueCB->queueSize - sizeof(UINT32)))) {//写时判断
306 return LOS_ERRNO_QUEUE_WRITE_SIZE_TOO_BIG;//塞进来的数据太大,大于队列节点能承受的范围
307 }
308 return LOS_OK;
309}
UINT16 queueState
Definition: los_queue_pri.h:90
这是这个函数的调用关系图:

◆ OsQueueReadParameterCheck()

STATIC LITE_OS_SEC_TEXT UINT32 OsQueueReadParameterCheck ( UINT32  queueID,
const VOID *  bufferAddr,
const UINT32 bufferSize,
UINT32  timeout 
)

读队列参数检查

在文件 los_queue.c196 行定义.

198{
199 if (GET_QUEUE_INDEX(queueID) >= LOSCFG_BASE_IPC_QUEUE_LIMIT) {//队列ID不能超上限
200 return LOS_ERRNO_QUEUE_INVALID;
201 }
202 if ((bufferAddr == NULL) || (bufferSize == NULL)) {//缓存地址和大小参数判断
203 return LOS_ERRNO_QUEUE_READ_PTR_NULL;
204 }
205
206 if ((*bufferSize == 0) || (*bufferSize > (OS_NULL_SHORT - sizeof(UINT32)))) {//限制了读取数据的上限64K, sizeof(UINT32)代表的是队列的长度
207 return LOS_ERRNO_QUEUE_READSIZE_IS_INVALID; //所以要减去
208 }
209
211
212 if (timeout != LOS_NO_WAIT) {//等待一定时间再读取
213 if (OS_INT_ACTIVE) {//如果碰上了硬中断
214 return LOS_ERRNO_QUEUE_READ_IN_INTERRUPT;//意思是:硬中断发生时是不能读消息队列的
215 }
216 }
217 return LOS_OK;
218}
STATIC INLINE VOID OsQueueDbgTimeUpdateHook(UINT32 queueID)
函数调用图:
这是这个函数的调用关系图:

◆ OsQueueWriteParameterCheck()

STATIC LITE_OS_SEC_TEXT UINT32 OsQueueWriteParameterCheck ( UINT32  queueID,
const VOID *  bufferAddr,
const UINT32 bufferSize,
UINT32  timeout 
)

写队列参数检查

在文件 los_queue.c220 行定义.

222{
223 if (GET_QUEUE_INDEX(queueID) >= LOSCFG_BASE_IPC_QUEUE_LIMIT) {//队列ID不能超上限
224 return LOS_ERRNO_QUEUE_INVALID;
225 }
226
227 if (bufferAddr == NULL) {//没有数据源
228 return LOS_ERRNO_QUEUE_WRITE_PTR_NULL;
229 }
230
231 if (*bufferSize == 0) {//这里没有限制写队列的大小,如果写入一个很大buf 会怎样?
232 return LOS_ERRNO_QUEUE_WRITESIZE_ISZERO;
233 }
234
236
237 if (timeout != LOS_NO_WAIT) {
238 if (OS_INT_ACTIVE) {
239 return LOS_ERRNO_QUEUE_WRITE_IN_INTERRUPT;
240 }
241 }
242 return LOS_OK;
243}
函数调用图:
这是这个函数的调用关系图:

变量说明

◆ g_freeQueueList

LITE_OS_SEC_BSS STATIC LOS_DL_LIST g_freeQueueList

空闲队列链表,管分配的,需要队列从这里申请

在文件 los_queue.c99 行定义.