近年来,国内开源完结跨越式发展,并成为企业提升创新能力、生产力、协作和透明度的要害。作为 OpenAtom OpenHarmony(以下简称“OpenHarmony”)开源项目共建单位之一,深开鸿以成为智能物联网操作体系领军者为战略目标,基于 OpenHarmony 聚焦智能物联网操作体系(KaihongOS)的技术研制与持续创新。

身为深开鸿 OS 内核开发师,咱们终年深耕于 OpenHarmony 的内核开发,希望经过共享一些工作上的经验,协助我们把握开源知识。

OpenHarmony LiteOS-M 内核是面向 IoT 领域构建的轻量级物联网操作体系内核,具有小体积、低功耗、高性能的特点,其代码结构简略,完结了进程、线程、内存等管理机制,供给了常见使命间 IPC、软定时器等公共模块,大幅度降低了嵌入式设备开发的难度。目前 OpenHarmony 的事情供给一种使命间的 IPC,即一个或多个使命能够经过写一个或多个不同的事情来触发内核调度,让另一个等候读取事情的使命进入运转状态,从而完结使命间的同步。

关于嵌入式开发工作人员和技术爱好者来说,深入了解常见使命间 IPC,有助于学习和研制内核。本文将从数据结构和算法解析 OpenHarmony 的事情机制,带我们深入了解内核使命间 IPC 原理。

要害数据结构

在解读事情的源码之前,首先了解下事情的要害的数据结构 PEVENT_CB_S:

typedef struct tagEvent {
    UINT32 uwEventID;       
    LOS_DL_LIST stEventList; /**< Event control block linked list */  
} EVENT_CB_S, *PEVENT_CB_S;

uwEventID:即符号使命的事情类型,每个bit能够标识一个事情,最多支撑 31 个事情(第 25bit 保存)。

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_CB_S *, 因而 eventCB 是指针。

阐明事情操控块由使命自己创立,内核事情模块只负责维护。使命界说自己的事情操控块变量,经过 LOS_EventInit 初始化,此时没有事情产生,事情链表为空。

用图来表达便是:

OpenHarmony—内核对象事件之源码详解

事情写操作

使命能够经过 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;
        }
    }
    ...
}  

1处,保存事情运用的或运算操作,因而一个或多个使命能够写一个或多个事情,写一次或屡次,而且每次为不同的事情,屡次写同一个事情相当于只写了一次;

2处,有事情产生了就该查看是否有使命在等候事情,事情链表不为空阐明有使命在等候事情;

3处,遍历事情链表,唤醒符合条件的使命。

LOS_DL_LIST_ENTRY((&eventCB->stEventList)->pstNext,LosTaskCB,pendList) 前面提到,头节点是空节点,第一次遍历从头节点的下一个节点开端,后续再顺次找出 nextTask,直到回到头节点;

4处,针对事情读取形式,找到满意条件的使命并唤醒该使命;

5处,一旦匹配到等候事情的使命,则执行使命调度,被唤醒的使命得到执行。

写事情实际操作如下图:

OpenHarmony—内核对象事件之源码详解

事情读操作

LiteOS 为用户供给了两个事情的函数:

● LOS_EventPoll():依据使命传入的事情值、掩码及校验形式,回来满意条件的事情,使命能够自动查看事情是否产生而不用被挂起;

● 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_WAITMODE_CLR来决议是否清除事情符号。

能够看出以上完结了两种事情的读取方式:一种是多个事情只要一个产生就算产生,另一种是悉数事情产生才算产生。

下面是 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
    } 
    ...
}  

1处,自动查询想要的事情是否现已产生;

2处,假如事情没有产生,就把当时使命挂起到等候事情链表中;

3处,假如事情没有产生,当时读事情的使命被挂起,让出 CPU;

4处,事情产生时等候事情的使命被调度再次获得 CPU 康复执行,读取事情。

事情读写整个进程串起来如下图所示:

OpenHarmony—内核对象事件之源码详解

事情销毁操作

干事有始有终,事情消费完结剩余的事情是清除事情和等候事情的使命链表。

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 中清空事情链表指针。

小结

看了上面的描绘,相信我们对 OpenHarmony LiteOS-M 内核事情的运作机制有了愈加深入的了解,开发者能够更好地运用事情的 API 来进行使命间的同步操作,也能够进一步尝试修正内核事情告诉机制,做出一个更适合自己使命的IPC机制。

OpenHarmony 生态建设离不开每位开发者的参与,希望有更多的开发者共享自己开源项目的经验和效果,共同为 OpenHarmony 生态建设奉献一份力气。

OpenHarmony—内核对象事件之源码详解