更多信息请访问:鸿蒙科技社区,与华为官方共建https://ost.51cto.com前言OpenHarmony的事件提供任务之间的同步机制。简单地说,一个或多个任务可以通过写入一个或多个不同的事件来触发内核调度,让另一个等待读取该事件的任务进入运行状态,从而实现任务之间的同步。具体是如何实现的?今天就带大家深入内核,扒一扒活动源码。关键数据结构在解读事件源码之前,有必要了解事件的关键数据结构PEVENT_CB_S。数据结构始终是内核学习中绕不开的一道坎:typedefstructtagEvent{UINT32uwEventID;LOS_DL_LISTstEventList;/**<事件控制块链表*/}EVENT_CB_S,*PEVENT_CB_S;uwEventID:标记任务的事件类型,每一位可以标识一个事件,最多支持31个事件(第25位保留)。stEventList:事件控制块的双向循环链表。理解这个字段是理解事件的关键。双向循环链表中唯一不变的节点是头节点,这里的stEventList就是头节点。当有任务在等待某个事件,但事件还没有发生时,该任务就会被挂载到等待列表中。当事件发生时,系统唤醒等待事件的任务,任务将从列表中移除。事件初始化下面是事件初始化源码:LITE_OS_SEC_TEXT_INITUINT32LOS_EventInit(PEVENT_CB_SeventCB){if(eventCB==NULL){returnLOS_ERRNO_EVENT_PTR_NULL;}eventCB->uwEventID=0;LOS_ListInit(&eventCB->stEventList);OsHookCall(LOS_HOOK_TYPE_EVENT_INIT,eventCB);returnLOS_OK;}PEVENT_CB_S等同于EVENT_CB_S*,所以eventCB是一个指针,一个指针,一个指针,说三遍很重要。侧面也说明事件控制块是由任务自己创建的,内核事件模块只负责维护。至此,大家就知道如何创建事件控制块了。任务定义自己的事件控制块变量,然后通过LOS_EventInit对其进行初始化。这时候没有事件发生,事件列表当然是空的。用图表示:事件写入操作任务可以通过LOS_EventWrite写入并触发一个或多个事件:---1if(!LOS_ListEmpty(&eventCB->stEventList)){---2for(resumedTask=LOS_DL_LIST_ENTRY((&eventCB->stEventList)->pstNext,LosTaskCB,pendList);&resumedTask->pendList!=(&eventCB->stEventList);){------3nextTask=LOS_DL_LIST_ENTRY(resumedTask->pendList.pstNext,LosTaskCB,pendList);if(((resumedTask->eventMode&LOS_WAITMODE_OR)&&(resumedTask->eventMask&events)!=0)||((resumedTask->eventMode&LOS_WAITMODE_AND)&&((resumedTask->eventMask&eventCB->uwEventID)==resumedTask->eventMask))){exitFlag=1;OsSchedTaskWake(resumedTask);---4}resumedTask=nextTask;}if(exitFlag==1){LOS_IntRestore(intSave);LOS_Schedule();---5返回LOS_OK;}}...}在1处,保存事件使用的OR运算,所以一个或多个A任务可以一次或多次写入一个或多个事件。当然,这是一个不同的事件。多次写入同一个事件相当于一次只写入2个地方。当一个事件发生时,你应该检查是否有任务在等待这个事件。事件如果链表不为空,说明有任务在等待事件。Step3,遍历事件链表,唤醒满足条件的任务。LOS_DL_LIST_ENTRY((&eventCB->stEventList)->pstNext,LosTaskCB,pendList)前面说过,头节点是一个空节点。第一次遍历从头节点的下一个节点开始,然后循着藤蔓逐个寻找nextTask,直到回到头节点。第四步,对于事件读取模式,找到一个满足条件的任务,唤醒该任务。第五步,一旦匹配到等待事件的任务,执行任务调度,执行被唤醒的任务。写事件的实际操作如下图所示:事件读取操作LiteOS为用户提供了两个事件读取函数:LOS_EventPoll():根据任务传入的事件值、掩码和验证方式,满足的事件返回条件,任务可以主动检查事件是否发生而不被挂起。LOS_EventRead():读一个事件,可以理解为阻塞读。如果事件没有发生,可以指定等待时间,暂停当前任务;以下是LOS_EventPoll()的实现:LITE_OS_SEC_TEXTUINT32LOS_EventPoll(UINT32*eventID,UINT32eventMask,UINT32mode){UINT32ret=0;UINT32intSave;如果(事件ID==NULL){返回LOS_ERRNO_EVENT_PTR_NULL;}intSave=LOS_IntLock();if(mode&LOS_WAITMODE_OR){if((*eventID&eventMask)!=0){---1ret=*eventID&eventMask;}}else{if((eventMask!=0)&&(eventMask==(*eventID&eventMask))){---2ret=*eventID&eventMask;}}if(ret&&(mode&LOS_WAITMODE_CLR)){---3*eventID=*eventID&~(ret);}LOS_IntRestore(intSave);returnret;}1、如果读取模式为LOS_WAITMODE_OR,只要有事件发生,如果读取成功,就会返回发生的事件。2、如果读取模式为LOS_WAITMODE_AND,只有所有的check事件都发生才认为读取成功,并返回所有发生的事件。3、事件读取成功后如何处理事件控制块中的事件标记?这里,LOS_WAITMODE_CLR用于判断是否清除事件标记。可以看出,上面实现了两种事件读取方式:多个事件只有一个发生时才发生,所有事件只有发生时才发生。下面是LOS_EventRead():LITE_OS_SEC_TEXTUINT32LOS_EventRead(PEVENT_CB_SeventCB,UINT32eventMask,UINT32mode,UINT32timeOut){...ret=LOS_EventPoll(&(eventCB->uwEventID),eventMask,mode);---1OsHookCall(LOS_HOOK_TYPE_EVENT_READ,eventCB,eventMask,mode,timeOut);如果(ret==0){如果(timeOut==0){LOS_IntRestore(intSave);返还;}if(g_losTaskLock){LOS_IntRestore(intSave);返回LOS_ERRNO_EVENT_READ_IN_LOCK;}runTsk=g_losTask.runTask;runTsk->eventMask=eventMask;runTsk->eventMode=模式;OsSchedTaskWait(&eventCB->stEventList,timeOut);---2LOS_IntRestore(intSave);LOS_Schedule();---3intSave=LOS_IntLock();if(runTsk->taskStatus&OS_TASK_STATUS_TIMEOUT){runTsk->task状态&=~OS_TASK_STATUS_TIMEOUT;LOS_IntRestore(intSave);返回LOS_ERRNO_EVENT_READ_TIMEOUT;}ret=LOS_EventPoll(&eventCB->uwEventID,eventMask,mode);---4}...}1、主动查询想要的事件是否发生2、如果事件没有发生,则当前任务暂停在等待事件链表中。在3,如果事件没有发生,则挂起当前读取事件的任务,释放CPU。4、事件发生时,调度等待事件的任务让CPU重新恢复执行,读取事件。事件读写的整个流程如下图所示:事件销毁操作有始有终,剩下的事件消费当然就是清除事件和等待事件的任务列表了。LITE_OS_SEC_TEXT_MINORUINT32LOS_EventClear(PEVENT_CB_SeventCB,UINT32eventMask){...eventCB->uwEventID&=eventMask;...}LITE_OS_SEC_TEXT_INITUINT32LOS_EventDestroy(PE??VENT_CB_SeventCB){...eventCB->stEventList.pstNext=(LOS_DL_LIST*)NULL;eventCB->stEventList.pstPrev=(LOS_DL_LIST*)NULL;...}通过在LOS_EventClear中设置eventMask=0来清除事件,在LOS_EventDestroy中清除事件列表指针。总结事件模块本身并不复杂。相信大家在看了上面的描述后,对事件的运行机制有了更深的理解。总结一下:事件控制块是由任务创建的,事件模块本身只维护事件控制块。内容。写事件会触发读事件任务被唤醒,任务调度就这么发生了。任务可以主动查询事件,也可以被动等待事件发生来唤醒自己。事件结束后,可根据应用场景选择性清除事件ID或(和)事件列表。更多信息请访问:与华为官方共建的鸿蒙技术社区https://ost.51cto.com
