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

鸿蒙LightKernelMCore源码解析系列之十二事件Event

时间:2023-03-21 21:03:40 科技观察

更多信息请访问:https://harmonyos.51cto.com,与华为官方共同打造的鸿蒙技术社区事件(Event)是一种机制任务间通信可用于任务间的同步。在多任务环境下,任务之间往往需要同步操作,一个等待就是一次同步。事件可以提供一对多和多对多的同步操作。本文通过分析鸿蒙LightKernel事件模块的源码,深入了解事件的使用。接下来我们看一下事件的结构,事件的初始化,以及事件常用操作的源码。一、事件结构体定义及常用宏定义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,只有当这些事件都发生了才能读取成功,否则任务会阻塞等待或返回错误代码。Eitherevent(LOS_WAITMODE_OR):Logicalor,根据接口传入的事件类型掩码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初始化事件在使用事件之前,必须使用函数UINT32LOS_EventInit(PEVENT_CB_SeventCB)来初始化事件。所需的参数是结构指针变量PEVENT_CB_SeventCB。分析代码,⑴表示传入的参数不能为空,否则返回错误码。(2)初始化事件code.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校验事件掩码我们可以使用函数UINT32LOS_EventPoll(UINT32*eventId,UINT32eventMask,UINT32mode)验证事件掩码,需要的参数是事件结构体的事件码eventId,用户传入的待验证的事件掩码eventMask,读模式mode,return用户传递的事件是否发生:当返回值为0时,表示用户期望的事件没有发生,否则表示用户期望的事件已经发生。我们看源码,首先检查(1)处传入参数的合法性,事件码不能为空。然后执行(2)处的代码进行验证。如果是任何事件读取方式,后面的判断并不代表至少有一个事件发生过,返回值ret表示发生了哪些事件。⑶如果是allthingsreading模式,当逻辑AND运算*eventId&eventMask仍然等于eventMask时,说明所有预期的事件都发生了,返回值ret表示哪些事件发生了。(4)当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_WAITMOD=(DE_OR)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.3Read/WriteEvent2.3.1读取指定事件类型我们可以使用函数LOS_EventRead()来读取事件,需要4个参数。eventCB为初始化的事件结构体,eventMask表示要读取的事件掩码,mode为上面解释的读取模式,timeout为读取超时时间,单位为Tick。当函数返回0时,表示预期的事件没有发生,读取事件失败,进入阻塞状态。返回非零表示预期的事件已经发生,并且事件已被成功读取。下面我们分析一下函数的源码,看看如何读取事件。(1)调用函数OsEventReadParamCheck()进行基本检查。比如第25位保留不能使用,事件掩码eventMask不能为0,读模式的组合是否合法等。⑵表示读事件不可中断。(3)调用验证函数OsEventPoll()检查事件eventMask是否发生。如果事件发生且ret不为0,则读取成功直接返回。ret为0,当事件没有发生时,执行(4),如果timeout超时为0,则调用者可以不等待,直接返回。(5)如果在锁任务调度过程中无法读取事件,则返回错误码。⑩更新当前任务的阻塞事件mask.eventMask和事件读取模式.eventMode。执行(7)调用函数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;⑺OsSchedestevent,OutEventCait->ListEventCaittime(&);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;}⑽retPollntCB->uwEventID,eventMask,mode);}LOS_IntRestore(intSave);returnret;}2.3.2写入指定事件类型我们可以使用函数UINT32LOS_EventWrite(PEVENT_CB_SeventCB,UINT32events)写入指定事件类型代码如下如图:下面我们分析一下源码,看看事件类型是怎么写的。(1)place代码对事件结构体的事件掩码和要写入的事件类型事件进行逻辑或计算,完成事件的写入。(2)如果等待事件的任务链表不为空,写事件后需要处理是否有任务可以读取对应的事件。(3)for循环依次遍历事件阻塞链表上的任务,(4)获取下一个任务nextTask。⑷处理不同的读模式,判断事件是否满足任务resumedTask读事件的要求,如果满足读事件,则执行⑩设置退出标志exitFlag,然后调用函数OsSchedTaskWake()改变任务的状态读取事件放入就绪队列,继续执行(7),遍历事件阻塞任务列表中的各个任务。⑻如果任务读取到事件,需要触发任务调度。lite_os_sec_textuint32los_eventwrite(pevent_cb_seventcb,uint32events){lostaskcb*resumedTask=null;null;lostaskcb*nextTask=(lostaskcb*)=null)||(eventcb->steventlist.pstprev==null)){returnlos_errno_event_not_initialized;}if(events&los_errtype_error){returnlos_errno_errno_errno_errno_event_settib_settib_settib_settib_invbit_invalid_invalideventcbelt;}intssever;}intssevert=losevert=losevert=losevent=loseventlock=los=loseventlock(los)⑵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清除事件我们可以使用函数UINT32LOS_EventClear(PEVENT_CB_SeventCB,UINT32eventMask)来清除指定的事件类型。下面分析一下源码,看看如何清除事件类型函数参数是事件结构体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销毁事件我们可以使用函数UINT32LOS_EventDestroy(PE??VENT_CB_SeventCB)来销毁指定的事件控制块。下面我们分析一下源码,看看如何销毁事件。函数参数是事件结构。销毁事件时,会先检查结构体参数是否为空,比较简单。(1)如果事件的任务阻塞链表不为空,则事件不能被销毁。(2)将事件结构的任务列表stEventList设置为空,完成事件的销毁。LITE_OS_SEC_TEXT_INITUINT32LOS_EventDestroy(PE??VENT_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;}Summary包含事件的结构,事件初始化,事件创建和删除,应用发布等更多信息请访问:Harmonyos.51cto.com,与华为官方合作打造的鸿蒙技术社区

最新推荐
猜你喜欢