当前位置: 首页 > 科技观察

鸿蒙LightKernelM核心源码分析系列之十三(续)消息队列QueueMail接口

时间:2023-03-20 13:40:35 科技观察

更多内容请访问:鸿蒙技术社区https://harmonyos.51cto.com与华为官方共建分析通过源码对队列(Queue),了解队列初始化、队列创建、删除、队列读写等操作。队列还提供了两个接口OsQueueMailAlloc和OsQueueMailFree。队列可以与静态内存池相关联。当一个任务从静态内存池申请内存块时,如果申请不到,则将该任务插入到队列的内存阻塞列表中。当其他任务释放内存时,该任务会分配内存块。接下来详细看一下这两个接口的源码。1.队列结构定义1.1队列结构定义让我们回忆一下队列结构的定义,在文件kernel\include\los_queue.h中定义队列控制块结构为LosQueueCB,该结构的源代码如下。需要查看成员变量memList。当任务无法从队列关联的静态内存池中申请到空闲内存块时,会将该任务插入到memList内存阻塞链表中,然后进行任务调度和切换。当其他任务向该静态内存池释放空闲内存块时,该任务申请空闲内存块,将任务从memList内存阻塞链表中移除,插入任务就绪队列,触发任务调度。typedefstruct{UINT8*queue;/**<队列内存空间指针*/UINT16queueState;/**<队列使用状态*/UINT16queueLen;/**<队列长度,即消息数*/UINT16queueSize;/**<消息节点大小*/UINT16queueID;/**<队列号*/UINT16queueHead;/**<消息头节点位置*/UINT16queueTail;/**<消息尾节点位置*/UINT16readWriteableCnt[OS_READWRITE_LEN];/**<2-dimensionalarray,可读写消息个数,0:可读,1:可写*/LOS_DL_LISTreadWriteList[OS_READWRITE_LEN];/**<二维双向链表数组,阻塞读写任务的双向链表,0:读链表,1:写链表*/LOS_DL_LISTmemList;/**<内存节点双向链表*/}LosQueueCB;2、QueueMail接口源码分析2.1OsQueueMailAlloc接口我们可以使用函数VOID*OsQueueMailAlloc(UINT32queueID,VOID*mailPool,UINT32timeOut)从和队列关联的静态内存池中申请空闲内存。下面我们分析一下源码,看看是如何申请内存的。该函数需要3个参数,queueID为正在使用的队列号,*mailPool为队列关联的静态内存池地址,timeOut为超时时间,值为[0,LOS_WAIT_FOREVER]。此接口函数返回请求的内存地址或NULL。⑴开始验证参数,⑵根据队列号获取队列控制结构体queueCB,然后验证队列是否在使用中。(3)调用静态内存分配函数LOS_MemboxAlloc获取空闲内存块,如果获取到的内存地址不为NULL则返回内存块地址,否则执行后续代码。(4)获取当前运行的任务控制结构,(5)将当前任务添加到队列的内存阻塞链表queueCB->memList中,然后触发任务调度。其他任务调用OsQueueMailFree释放内存后,上述阻塞任务获得内存块,或超时退出阻塞列表并调度操作,然后开始执行语句⑴。⑺表示任务超时返回没有获取到内存块,跳转到END标签返回NULL内存地址。⑻表示已经获取到内存块,任务的msg置空,返回获取到的内存块地址。LITE_OS_SEC_TEXTVOID*OsQueueMailAlloc(UINT32queueID,VOID*mailPool,UINT32timeOut){VOID*mem=(VOID*)NULL;UINT32intSave;LosQueueCB*queueCB=(LosQueueCB*)NULL;LosTaskCB*runTsk=(LosTaskCB*)NULL;⑴=if(queueID>LOSCFG_BASE_IPC_QUEUE_LIMIT){returnNULL;}if(mailPool==NULL){returnNULL;}if(timeOut!=LOS_NO_WAIT){if(OS_INT_ACTIVE){returnNULL;}}intSave=LOS_IntLock();⑵queueCB=GET_QUEUE_HANDLE(queueID);if(queueCB->queueState==OS_QUEUE_UNUSED){gotoEND;}⑶mem=LOS_MemboxAlloc(mailPool);if(mem==NULL){if(timeOut==LOS_NO_WAIT){gotoEND;}⑷runTsk=(LosTaskCB*)g_losTask.runTask;⑸OsSchedTaskWait(&queueCB->memList,timeOut);LOS_IntRestore(intSave);LOS_Schedule();⑴intSave=LOS_IntLock();if(runTsk->taskStatus&OS_TASK_STATUS_TIMEOUT){⑺runTsk->taskStatus&=(~OS_TASK_STATUS_TIMEOUT);gotoEND;}else{/*当进入当前分支时,意味着当前任务已经可用,*sotherunTsk->msgcannotbeNULL.*/⑻mem=runTsk->msg;runTsk->msg=NULL;}}END:LOS_IntRestore(intSave);returnmem;}2.2OsQueueMailFree我们可以使用函数UINT32OsQueueMailFree(UINT32queueID,VOID*mailPool,VOID*mailMem)释放空闲内存到与队列关联的静态内存池。下面分析一下源码,看看如何释放内存这个函数需要3个参数,queueID是正在使用的一个队列的编号,*mailPool是队列关联的静态内存池的地址,*mailMem是内存的地址块被释放。该接口的返回值类型为无符号整数,表示成功或错误码。⑴开始检查参数。(2)调用静态内存释放函数LOS_MemboxFree释放空闲内存块。如果发布失败,则返回错误代码。(3)根据队列号获取队列控制结构体queueCB,然后查看队列是否在使用中。内存释放成功后,如果队列的内存阻塞列表不为空且有阻塞任务,则执行(4)。(5)从阻塞列表中获取第一个任务控制结构,然后调用接口OsSchedTaskWake将任务从阻塞列表中移除,加入到任务就绪队列中。⑴从静态内存池中申请一块内存块。如果申请失败,返回错误码,否则执行⑺,将申请的内存赋值给任务控制结构的msg成员变量,然后触发调度。LITE_OS_SEC_TEXTUINT32OsQueueMailFree(UINT32queueID,VOID*mailPool,VOID*mailMem){VOID*mem=(VOID*)NULL;UINT32intSave;LosQueueCB*queueCB=(LosQueueCB*)NULL;LosTaskCB*resumedTask=(LosTaskCB*)NULL;>⑴if(queueIDLOSCFG_BASE_IPC_QUEUE_LIMIT){returnLOS_ERRNO_QUEUE_MAIL_HANDLE_INVALID;}if(mailPool==NULL){returnLOS_ERRNO_QUEUE_MAIL_PTR_INVALID;}intSave=LOS_IntLock();⑵if(LOS_MemboxFree(mailPool,mailMem)){LOS_IntRestore(intSave);returnLOS_ERRNO_QUEUE_MAIL_FREE_ERROR;}⑶queueCB=GET_QUEUE_HANDLE(queueID);if(queueCB->queueState==OS_QUEUE_UNUSED){LOS_IntRestore(intSave);returnLOS_ERRNO_QUEUE_NOT_CREATE;}⑷if(!LOS_ListEmpty(&queueCB->memList)){⑸resumedTask=OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&queueCB->memList));OsSchedTaskWake(resumedTask);⑹mem=LOS_MemboxAlloc(mailPool);if(mem==NULL){LOS_IntRestore(intSave);returnLOS_ERRNO_QUEUE_NO_MEMORY;}⑺resumedTask->msg=mem;LOS_IntRestore(intSave);LOS_Schedule();}else{LOS_IntRestore(intSave);}returnLOS_Ok;}总结本文带领大家分析一下鸿蒙轻内核.com的队列模块中QueueMail的两个接口的源码