OpenHarmony 的同步机制,简单来说就是一个或多个任务可以通过一个或多个事件来触发事件安排不同的等待写入事件任务进入运行状态,提供实现任务的同步代码。具体是怎么实现的呢?
关键数据结构
在查看事件的源代码之前还是先了解事件的关键数据结构PEVENT_CB_S,数据结构永远是内核学习不开的坎:
typedef struct tagEvent {
UINT32 uwEventiD;
LOS_DL_LIST stEventList; /**< Event control block linked list */
} EVENT_CB_S, *PEVENT_CB_S;
uwEventID:标记任务的事件类型,每个位可以最多支持一个事件(第225位保留)。 stEventList:事件控制块的循环链表,理解表个事件是事件的关键。在循环理解链中唯一不变的节点头节点,而这里的stEventList就是头节点。当有任务等待事件加载到事件发生时任务会被挂等待到链表中,当事件发生时系统启动等待事件的任务,此时任务会被剔出链表。
##活动初始化##
下面是事件初始化源:
LITE_OS_SEC_TEXT_INIT UINT32 LOS_EventInit(PEVENT_CB_S eventCB)
{
if (eventCB == NULL) {
return LOS_ERRNO_EVENT_PTR_NULL;
}
eventCB->uwEventID = 0;
LOS_ListInit(&eventCB->stEventList);
OsHookCall(LOS_HOOK_TYPE_EVENT_INIT, eventCB);
return LOS_OK;
}
PEVENT_CB_S相当于EVENT_S *,因此事件CB到这里是***,也是CB的靶子,是重要的,据说是三个控制块。期间 事件由任务自己创建说明,内核事件模块只负责维护。块吧,任务定义自己的事件变量块,然后通过LOS_EventInit来控制控制链,此时没有事件发生,当然事件表空如。用图来表达 就是:
##事件写操作##
任务可以通过LOS_EventWrite来写触发一个或多个事件:
LITE_OS_SEC_TEXT UINT32 LOS_EventWrite(PEVENT_CB_S eventCB, UINT32 events)
{
...
eventCB->uwEventID |= events; ---1
if (!LOS_ListEmpty(&eventCB->stEventList)) { ---2
for (resumedTask = LOS_DL_LIST_ENTRY((&eventCB->stEventList)->pstNext, LosTaskCB, pendList);
&resumedTask->pendList != (&eventCB->stEventList);) { -------3
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); ---4
}
resumedTask = nextTask;
}
if (exitFlag == 1) {
LOS_IntRestore(intSave);
LOS_Schedule(); ---5
return LOS_OK;
}
}
...
}
2. 2 处,使用一次事件或一次操作,因此可以写一个或多个多次,通常是不同的事件,通常写一个事件的只写一次; 2 处,有发生事件就是否有任务在等待事件,事件链表不为空说明任务在等待事件; 3处,遍历事件链表,触发条件的任务。 LOS_DL_LIST_ENTRY((&eventCB->stEventList)-> pstNext, LosTaskCB, 追踪器) 前面是头号节点,第一次是从空头节点的下一个节点列表,顺藤摸瓜,然后顺藤摸瓜,开始追踪任务,直到任务,直到找到节点; 4,针对搜索模式,找到目标;的任务并由该任务负责; 5 处,匹配到等待事件的任务,执行任务调度,被授权执行。
写事件实际操作如下图:
##事件读操作## LiteOS为用户提供了两个事件读函数:
LOS():根据事件响应的情况下发生报警的值、码及报警模式,返回任务可以检测是否发生而被挂起;
LOS_EventRead():读取事件,可以理解为破坏式读取,如果事件没有发生,可以指定等待时间,挂起当前任务;
下面是LOS_EventPoll()的实现:
LITE_OS_SEC_TEXT UINT32 LOS_EventPoll(UINT32 *eventID, UINT32 eventMask, UINT32 mode)
{
UINT32 ret = 0;
UINT32 intSave;
if (eventID == NULL) {
return LOS_ERRNO_EVENT_PTR_NULL;
}
intSave = LOS_IntLock();
if (mode & LOS_WAITMODE_OR) {
if ((*eventID & eventMask) != 0) { ---1
ret = *eventID & eventMask;
}
} else {
if ((eventMask != 0) && (eventMask == (*eventID & eventMask))) { ---2
ret = *eventID & eventMask;
}
}
if (ret && (mode & LOS_WAITMODE_CLR)) { ---3
*eventID = *eventID & ~(ret);
}
LOS_IntRestore(intSave);
return ret;
}
1,如果读取模式是LOS_WAITMODE_OR,只要有一个事件发生,则读取成功,返回发生的那个事件; 2,如果读取模式是LOS_WAITMODE_AND,检查事件才发生读取成功,并返回全部发生事件; 3 是否读取成功后事件控制块标记,事件处理方式如何? LOS_MODE_CL 事件标记 。和全部事件发生才算发生。
下面是 LOS_EventRead()
LITE_OS_SEC_TEXT UINT32 LOS_EventRead(PEVENT_CB_S eventCB, UINT32 eventMask, UINT32 mode, UINT32 timeOut)
{
...
ret = LOS_EventPoll(&(eventCB->uwEventID), eventMask, mode); ---1
OsHookCall(LOS_HOOK_TYPE_EVENT_READ, eventCB, eventMask, mode, timeOut);
if (ret == 0) {
if (timeOut == 0) {
LOS_IntRestore(intSave);
return ret;
}
if (g_losTaskLock) {
LOS_IntRestore(intSave);
return LOS_ERRNO_EVENT_READ_IN_LOCK;
}
runTsk = g_losTask.runTask;
runTsk->eventMask = eventMask;
runTsk->eventMode = mode;
OsSchedTaskWait(&eventCB->stEventList, timeOut); ---2
LOS_IntRestore(intSave);
LOS_Schedule(); ---3
intSave = LOS_IntLock();
if (runTsk->taskStatus & OS_TASK_STATUS_TIMEOUT) {
runTsk->taskStatus &= ~OS_TASK_STATUS_TIMEOUT;
LOS_IntRestore(intSave);
return LOS_ERRNO_EVENT_READ_TIMEOUT;
}
ret = LOS_EventPoll(&eventCB->uwEventID, eventMask, mode); ---4
}
...
}
2处,如果想要查询是否已经发生,当前任务是否发生; 当前任务没有挂起; 3 没有事件发生处,当前事件的任务被挂起,让出 CPU; 4 处,事件发生时等待事件的任务被重新安排获取 CPU 恢复执行,读取事件。
事件学习整个过程串起来如下图所示:
##事件事件事件事件表##事件事件有事件 结束,任务完成剩余的事件是清除和等待的事件。
LITE_OS_SEC_TEXT_MINOR UINT32 LOS_EventClear(PEVENT_CB_S eventCB, UINT32 eventMask)
{
...
eventCB->uwEventID &= eventMask;
...
}
LITE_OS_SEC_TEXT_INIT UINT32 LOS_EventDestroy(PEVENT_CB_S eventCB)
{
...
eventCB->stEventList.pstNext = (LOS_DL_LIST *)NULL;
eventCB->stEventList.pstPrev = (LOS_DL_LIST *)NULL;
...
}
在LOS_EventClear中通过使eventMask=0来清空事件,在LOS_EventDestroy中清空事件链表指针。
##小结## 事件剧情本身并不描述,大伙对事件的理解机制已经让我们更加深刻的,下面来个复杂的情节:
事件控制块由任务创建,事件模块本身只维护事件控制块的内容;
写事件会触发读事件任务被触发,任务调度就这样发生;
任务可以主动事件,可以被动等待事件发生时也可以主动调用自己;
事件结束后根据应用场景可以选择清除事件ID或(和)事件链表。
|