event(事件Event的含义和作用)

发布时间:2025-12-10 22:46:40 浏览次数:1

事件(Event)是一种任务间通信的机制,可用于任务间的同步。多任务环境下,任务之间往往需要同步操作,一个等待即是一个同步。事件可以提供一对多、多对多的同步操作。本文通过分析鸿蒙轻内核事件模块的源码,深入掌握事件的使用。本文中所涉及的源码,以OpenHarmony LiteOS-M内核为例,均可以在开源站点https://gitee.com/openharmony/kernel_liteos_m 获取。

接下来,我们看下事件的结构体,事件初始化,事件常用操作的源代码。

1、事件结构体定义和常用宏定义

1.1 事件结构体定义

在文件kernel\include\los_event.h定义的事件控制块结构体为EVENT_CB_S,结构体源代码如下,结构体成员的解释见注释部分。

typedefstructtagEvent{UINT32uwEventID;/**<事件ID,每一位标识一种事件类型*/LOS_DL_LISTstEventList;/**<读取事件的任务链表*/}EVENT_CB_S,*PEVENT_CB_S;

1.2 事件常用宏定义

在读事件时,可以选择读取模式。读取模式由如下几个宏定义:

所有事件(LOS_WAITMODE_AND):

逻辑与,基于接口传入的事件类型掩码eventMask,只有这些事件都已经发生才能读取成功,否则该任务将阻塞等待或者返回错误码。

任一事件(LOS_WAITMODE_OR):

逻辑或,基于接口传入的事件类型掩码eventMask,只要这些事件中有任一种事件发生就可以读取成功,否则该任务将阻塞等待或者返回错误码。

清除事件(LOS_WAITMODE_CLR):

这是一种附加读取模式,需要与所有事件模式或任一事件模式结合使用(LOS_WAITMODE_AND | LOS_WAITMODE_CLR或 LOS_WAITMODE_OR | LOS_WAITMODE_CLR)。在这种模式下,当设置的所有事件模式或任一事件模式读取成功后,会自动清除事件控制块中对应的事件类型位。

#defineLOS_WAITMODE_AND(4)#defineLOS_WAITMODE_OR(2)#defineLOS_WAITMODE_CLR(1)

2、事件常用操作

2.1 初始化事件

在使用事件前,必须使用函数UINT32 LOS_EventInit(PEVENT_CB_S eventCB)来初始化事件,需要的参数是结构体指针变量PEVENT_CB_S eventCB。分析下代码,⑴处表示传入的参数不能为空,否则返回错误码。⑵处把事件编码.uwEventID初始化为0,然后初始化双向循环链表.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);returnLOS_OK;}

2.2 校验事件掩码

我们可以使用函数UINT32 LOS_EventPoll(UINT32 *eventId, UINT32 eventMask, UINT32 mode)来校验事件掩码,需要的参数为事件结构体的事件编码eventId、用户传入的待校验的事件掩码eventMask及读取模式mode,返回用户传入的事件是否发生: 返回值为0时,表示用户预期的事件没有发生,否则表示用户期望的事件发生。

我们看下源码,⑴处先检查传入参数的合法性,事件编码不能为空。然后执行⑵处的代码进行校验。如果是任一事件读取模式,接下来的判断不等于表示至少有一个事件发生了,返回值ret就表示哪些事件发生了。⑶如果是所有事情读取模式,当逻辑与运算*eventId & eventMask还等于eventMask时,表示期望的事件全部发生了,返回值ret就表示哪些事件发生了。⑷处当ret不为0,期望的事件发生,并且是清除事件读取模式时,需要把已经发生的事情进行清除。看来,这个函数不仅仅是查询事件有没有发生,还会有更新事件编码的动作。

LITE_OS_SEC_TEXTUINT32LOS_EventPoll(UINT32*eventID,UINT32eventMask,UINT32mode){UINT32ret=0;UINT32intSave;⑴if(eventID==NULL){returnLOS_ERRNO_EVENT_PTR_NULL;}intSave=LOS_IntLock();⑵if(mode&LOS_WAITMODE_OR){if((*eventID&eventMask)!=0){ret=*eventID&eventMask;}}else{⑶if((eventMask!=0)&&(eventMask==(*eventID&eventMask))){ret=*eventID&eventMask;}}⑷if(ret&&(mode&LOS_WAITMODE_CLR)){*eventID=*eventID&~(ret);}LOS_IntRestore(intSave);returnret;}

2.3 读/写事件

2.3.1 读取指定事件类型

我们可以使用函数LOS_EventRead()来读取事件,需要4个参数。eventCB是初始化好的事件结构体,eventMask表示需要读取的事件掩码,mode是上文说明过的读取模式,timeout是读取超时,单位是Tick。函数返回0时,表示期望的事件没有发生,读取事件失败,进入阻塞。返回非0时表示期望的事件发生了,成功读取事件。下面我们分析下函数的源码来看看如何读取事件的。

⑴处调用函数OsEventReadParamCheck()进行基础的校验,比如第25位保留不能使用,事件掩码eventMask不能为零,读取模式组合是否合法。⑵处表示不能中断中读取事件。⑶处调用校验函数OsEventPoll()检查事件eventMask是否发生。如果事件发生ret不为0,成功读取直接返回。ret为0,事件没有发生时,执行⑷,如果超时时间timeout为0,调用者不能等待时,直接返回。⑸如果锁任务调度时,不能读取事件,返回错误码。

⑹更新当前任务的阻塞的事件掩码.eventMask和事件读取模式.eventMode。执行⑺调用函数OsSchedTaskWait更改当前任务的状态为阻塞状态,挂载到事件的任务阻塞链表上。如果timeout不是永久等待,还会把任务设置为OS_TASK_STATUS_PEND_TIME状态并设置等待时间。⑻处触发任务调度,后续程序需要等到读取到事件才会继续执行。

⑼如果等待时间超时,事件还不可读,本任务读取不到指定的事件时,返回错误码。如果可以读取到指定的事件时,执行⑽,检查事件eventMask是否发生,然后返回结果值。

LITE_OS_SEC_TEXTUINT32LOS_EventRead(PEVENT_CB_SeventCB,UINT32eventMask,UINT32mode,UINT32timeOut){UINT32ret;UINT32intSave;LosTaskCB*runTsk=NULL;⑴ret=OsEventReadParamCheck(eventCB,eventMask,mode);if(ret!=LOS_OK){returnret;}⑵if(OS_INT_ACTIVE){returnLOS_ERRNO_EVENT_READ_IN_INTERRUPT;}intSave=LOS_IntLock();⑶ret=LOS_EventPoll(&(eventCB->uwEventID),eventMask,mode);OsHookCall(LOS_HOOK_TYPE_EVENT_READ,eventCB,eventMask,mode);if(ret==0){⑷if(timeOut==0){LOS_IntRestore(intSave);returnret;}⑸if(g_losTaskLock){LOS_IntRestore(intSave);returnLOS_ERRNO_EVENT_READ_IN_LOCK;}runTsk=g_losTask.runTask;⑹runTsk->eventMask=eventMask;runTsk->eventMode=mode;⑺OsSchedTaskWait(&eventCB->stEventList,timeOut);LOS_IntRestore(intSave);⑻LOS_Schedule();⑼intSave=LOS_IntLock();if(runTsk->taskStatus&OS_TASK_STATUS_TIMEOUT){runTsk->taskStatus&=~OS_TASK_STATUS_TIMEOUT;LOS_IntRestore(intSave);returnLOS_ERRNO_EVENT_READ_TIMEOUT;}⑽ret=LOS_EventPoll(&eventCB->uwEventID,eventMask,mode);}LOS_IntRestore(intSave);returnret;}

2.3.2 写入指定的事件类型

我们可以使用函数UINT32 LOS_EventWrite(PEVENT_CB_S eventCB, UINT32 events)来写入指定的事件类型。代码如下所示:

下面通过分析源码来看看如何写入事件类型的。⑴处代码把事件结构体的事件掩码和要写入的事件类型events进行逻辑或计算,来完成事件的写入。⑵如果等待事件的任务链表不为空,需要处理写入事件后是否有任务能读取到相应的事件。⑶处for循环依次遍历事件阻塞链表上的任务,⑷获取下一个任务nextTask。⑸处

分不同的读取模式判断事件是否符合任务resumedTask读取事件的要求,如果满足读取事件,执行⑹设置退出标记exitFlag,然后调用函数OsSchedTaskWake()把读取事件的任务更改状态并放入就绪队列,继续执行⑺,遍历事件的阻塞任务链表中的每一个任务。⑻如果有任务读取到事件,需要触发任务调度。

LITE_OS_SEC_TEXTUINT32LOS_EventWrite(PEVENT_CB_SeventCB,UINT32events){LosTaskCB*resumedTask=NULL;LosTaskCB*nextTask=(LosTaskCB*)NULL;UINT32intSave;UINT8exitFlag=0;if(eventCB==NULL){returnLOS_ERRNO_EVENT_PTR_NULL;}if((eventCB->stEventList.pstNext==NULL)||(eventCB->stEventList.pstPrev==NULL)){returnLOS_ERRNO_EVENT_NOT_INITIALIZED;}if(events&LOS_ERRTYPE_ERROR){returnLOS_ERRNO_EVENT_SETBIT_INVALID;}intSave=LOS_IntLock();⑴eventCB->uwEventID|=events;OsHookCall(LOS_HOOK_TYPE_EVENT_WRITE,eventCB);⑵if(!LOS_ListEmpty(&eventCB->stEventList)){⑶for(resumedTask=LOS_DL_LIST_ENTRY((&eventCB->stEventList)->pstNext,LosTaskCB,pendList);&resumedTask->pendList!=(&eventCB->stEventList);){⑷nextTask=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);}⑺resumedTask=nextTask;}if(exitFlag==1){LOS_IntRestore(intSave);⑻LOS_Schedule();returnLOS_OK;}}LOS_IntRestore(intSave);returnLOS_OK;}

2.4 清除事件

我们可以使用函数UINT32 LOS_EventClear(PEVENT_CB_S eventCB, UINT32 eventMask)来清除指定的事件类型,下面通过分析源码看看如何清除事件类型的。

函数参数为事件结构体eventCB和要清除的事件类型eventMask。清除事件时首先会进行结构体参数是否为空的校验,这些比较简单。⑴处把事件结构体的事件掩码和要清除的事件类型eventMask进行逻辑与计算,来完成事件的清理。

LITE_OS_SEC_TEXT_MINORUINT32LOS_EventClear(PEVENT_CB_SeventCB,UINT32eventMask){UINT32intSave;if(eventCB==NULL){returnLOS_ERRNO_EVENT_PTR_NULL;}intSave=LOS_IntLock();⑴eventCB->uwEventID&=eventMask;LOS_IntRestore(intSave);OsHookCall(LOS_HOOK_TYPE_EVENT_CLEAR,eventCB);returnLOS_OK;}

2.5 销毁事件

我们可以使用函数UINT32 LOS_EventDestroy(PEVENT_CB_S eventCB)来销毁指定的事件控制块,下面通过分析源码看看如何销毁事件的。

函数参数为事件结构体,销毁事件时首先会进行结构体参数是否为空的校验,这些比较简单。⑴处如果事件的任务阻塞链表不为空,则不能销毁事件。⑵把事件结构体的读取事件的任务链表stEventList设置为空,完成事件的销毁。

LITE_OS_SEC_TEXT_INITUINT32LOS_EventDestroy(PEVENT_CB_SeventCB){UINT32intSave;if(eventCB==NULL){returnLOS_ERRNO_EVENT_PTR_NULL;}intSave=LOS_IntLock();⑴if(!LOS_ListEmpty(&eventCB->stEventList)){LOS_IntRestore(intSave);returnLOS_ERRNO_EVENT_SHOULD_NOT_DESTORY;}⑵eventCB->stEventList.pstNext=(LOS_DL_LIST*)NULL;eventCB->stEventList.pstPrev=(LOS_DL_LIST*)NULL;LOS_IntRestore(intSave);OsHookCall(LOS_HOOK_TYPE_EVENT_DESTROY);returnLOS_OK;}

“事件Event的含义和作用”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注本站网站,小编将为大家输出更多高质量的实用文章!

event
需要做网站?需要网络推广?欢迎咨询客户经理 13272073477