鸿蒙轻内核M核源码分析系列十二 事件Event 原创 精华
鸿蒙轻内核M核源码分析系列十二 事件Event
事件(Event
)是一种任务间通信的机制,可用于任务间的同步。多任务环境下,任务之间往往需要同步操作,一个等待即是一个同步。事件可以提供一对多、多对多的同步操作。本文通过分析鸿蒙轻内核事件模块的源码,深入掌握事件的使用。本文中所涉及的源码,以OpenHarmony LiteOS-M
内核为例,均可以在开源站点https://gitee.com/openharmony/kernel_liteos_m 获取。
接下来,我们看下事件的结构体,事件初始化,事件常用操作的源代码。
1、事件结构体定义和常用宏定义
1.1 事件结构体定义
在文件kernel\include\los_event.h
定义的事件控制块结构体为EVENT_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
)。在这种模式下,当设置的所有事件模式或任一事件模式读取成功后,会自动清除事件控制块中对应的事件类型位。
3、事件常用操作
3.1 初始化事件
在使用事件前,必须使用函数UINT32 LOS_EventInit(PEVENT_CB_S eventCB)
来初始化事件,需要的参数是结构体指针变量PEVENT_CB_S eventCB
。分析下代码,⑴处表示传入的参数不能为空,否则返回错误码。⑵处把事件编码.uwEventID
初始化为0,然后初始化双向循环链表.stEventList
,用于挂载读取事件的任务。
3.2 校验事件掩码
我们可以使用函数UINT32 LOS_EventPoll(UINT32 *eventId, UINT32 eventMask, UINT32 mode)
来校验事件掩码,需要的参数为事件结构体的事件编码eventId
、用户传入的待校验的事件掩码eventMask
及读取模式mode
,返回用户传入的事件是否发生: 返回值为0时,表示用户预期的事件没有发生,否则表示用户期望的事件发生。
我们看下源码,⑴处先检查传入参数的合法性,事件编码不能为空。然后执行⑵处的代码进行校验。如果是任一事件读取模式,接下来的判断不等于表示至少有一个事件发生了,返回值ret
就表示哪些事件发生了。⑶如果是所有事情读取模式,当逻辑与运算*eventId & eventMask
还等于eventMask
时,表示期望的事件全部发生了,返回值ret
就表示哪些事件发生了。⑷处当ret
不为0,期望的事件发生,并且是清除事件读取模式时,需要把已经发生的事情进行清除。看来,这个函数不仅仅是查询
事件有没有发生,还会有更新
事件编码的动作。
3.3 读/写事件
3.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
是否发生,然后返回结果值。
3.3.2 写入指定的事件类型
我们可以使用函数UINT32 LOS_EventWrite(PEVENT_CB_S eventCB, UINT32 events)
来写入指定的事件类型。代码如下所示:
下面通过分析源码来看看如何写入事件类型的。⑴处代码把事件结构体的事件掩码和要写入的事件类型events
进行逻辑或计算,来完成事件的写入。⑵如果等待事件的任务链表不为空,需要处理写入事件后是否有任务能读取到相应的事件。⑶处for
循环依次遍历事件阻塞链表上的任务,⑷获取下一个任务nextTask
。⑸处
分不同的读取模式判断事件是否符合任务resumedTask
读取事件的要求,如果满足读取事件,执行⑹设置退出标记exitFlag
,然后调用函数OsSchedTaskWake()
把读取事件的任务更改状态并放入就绪队列,继续执行⑺,遍历事件的阻塞任务链表中的每一个任务。⑻如果有任务读取到事件,需要触发任务调度。
3.4 清除事件
我们可以使用函数UINT32 LOS_EventClear(PEVENT_CB_S eventCB, UINT32 eventMask)
来清除指定的事件类型,下面通过分析源码看看如何清除事件类型的。
函数参数为事件结构体eventCB
和要清除的事件类型eventMask
。清除事件时首先会进行结构体参数是否为空的校验,这些比较简单。⑴处把事件结构体的事件掩码和要清除的事件类型eventMask
进行逻辑与计算,来完成事件的清理。
3.5 销毁事件
我们可以使用函数UINT32 LOS_EventDestroy(PEVENT_CB_S eventCB)
来销毁指定的事件控制块,下面通过分析源码看看如何销毁事件的。
函数参数为事件结构体,销毁事件时首先会进行结构体参数是否为空的校验,这些比较简单。⑴处如果事件的任务阻塞链表不为空,则不能销毁事件。⑵把事件结构体的读取事件的任务链表stEventList
设置为空,完成事件的销毁。
小结
本文带领大家一起剖析了鸿蒙轻内核的事件模块的源代码,包含事件的结构体、事件初始化、事件创建删除、申请释放等。感谢阅读,如有任何问题、建议,都可以留言给我们: https://gitee.com/openharmony/kernel_liteos_m/issues 。为了更容易找到鸿蒙轻内核代码仓,建议访问 https://gitee.com/openharmony/kernel_liteos_m ,关注Watch
、点赞Star
、并Fork
到自己账户下,谢谢。
好文,楼主厉害了!