1.前言OpenAtomOpenHarmony(以下简称“OpenHarmony”)是由OpenAtom开源基金会(OpenAtomFoundation)孵化和运营的开源项目,在全智能时代,以开源方式为基础,构建智能终端设备操作系统的框架和平台,推动万物互联产业的繁荣发展。OpenHarmony作为全场景、全连接、全智能的分布式泛终端操作系统,集成了各种终端设备的能力,实现硬件互助和资源共享,为用户提供流畅的全场景体验。为了适配各种硬件,OpenHarmony提供了LiteOS和Linux内核,并在这些内核的基础上形成了不同的系统类型,同时在这些系统中构建了一套统一的系统能力。OpenHarmonyLiteOS-M内核是为物联网领域打造的轻量级物联网操作系统内核。LiteOS-M内核为任务间通信提供了多种机制,包括队列、事件、互斥量和信号量。每种机制涉及哪些关键数据结构?这些数据结构是如何工作的?接下来我将从队列、事件、互斥量、信号量等几个内核对象入手,来讲解内核IPC机制的数据结构。2.数据结构--队列队列,又称消息队列,是一种常用于任务间通信的数据结构,可以在任务间传递消息内容或消息地址。内核使用队列控制块来管理消息队列,同时使用双向循环链表来管理控制块。队列控制块:管理特定消息队列的数据块。内核初始化时调用OsQueueInit()创建,依次挂载到双向环链表g_freeQueueList。此时控制块的状态为OS\_QUEUE\_UNUSED,队列控制块用于保存队列的状态、队列长度、消息长度、队列ID、队列头尾位置、列表等待读取和写入的任务。内核管理消息队列和任务,根据这些信息完成消息的读写等操作。typedefstruct{UINT8*队列;UINT16队列状态;UINT16队列长度;UINT16队列大小;UINT16队列ID;UINT16队列头;UINT16队列尾部;UINT16readWriteableCnt[OS_READWRITE_LEN];LOS_DL_LISTreadWriteList[OS_READWRITE_LEN];LOS_DL_LIST内存列表;}LosQueueCB; 初始化后队控制Block的组织结构如下:创建队列:队列用于存储具体的消息内容,任务可以调用LOS\_QueueCreate()创建队列。此时内核会根据入参指定的队列长度和消息大小申请内存创建队列,并从g\_freeQueueList中分配一个控制块来管理队列,分配的队列控制的状态块是OS\_QUEUE\_INUSED。分配队列控制块时,总是从头节点开始,如下图,首先分配控制块0,用于管理新创建的队列。写队列:内核支持两种写队列方式:从尾写到LOS\_QueueWrite()和从头写到LOS\_QueueWriteHead():读队列:读队列只有一种方式,读LOS\_QueueRead从队列head()中,head读取后指向下一个节点。删除队列:当队列不再使用时,可以使用LOS\_QueueDelete()删除队列。此时队列控制块会返回到g_freeQueueList,消息队列会被释放:3.数据结构--事件用于实现任务间通信同步,但是事件通信只能是事件类型的通信,无数据传输,事件控制块由任务申请,内核负责维护。事件控制块:事件控制块用于记录事件和管理等待读取事件的任务。uwEventID一共32位代表31个事件(bit25保留)。stEventList事件控制块的双向循环链表。当有任务读取事件但事件还没有发生时,任务会被挂载到链表中。当事件发生时,系统唤醒并等待事件的发生。任务,此时任务会被从链表中取出。typedefstructtagEvent{UINT32uwEventID;LOS_DL_LISTstEventList;}EVENT_CB_S,*PEVENT_CB_S;事件初始化:任务创建事件控制块,然后调用LOS_EventInit()进行初始化,初始化后状态如下:事件读取:当事件没有发生时,读取事件操作会触发系统调度,挂起当前任务并将其添加到stEventList链表中。下图中,事件1发生,任务Task1读取事件2,但是事件2没有发生,Task1被挂起。事件写入:当事件2发生时,任务Task2将事件2写入uwEventID,此时任务Task1被调度读取事件成功,事件2对应的位清0(或不清0),Task1读取链表stEventList中的事件被提取。事件删除:事件控制块由任务创建,内核不负责控制块的删除,但任务可以调用LOS\_EventClear来清除事件。4、数据结构——互斥锁互斥锁,又称互斥信号量,是一种特殊的二进制信号量,用于实现对共享资源的独占处理。互斥量在任何时候的状态只有解锁或锁定。当有任务持有互斥锁时,互斥锁处于锁定状态,任务获得互斥锁的所有权;当任务释放它时,互斥锁被解锁,任务失去互斥锁的所有权;当一个任务持有互斥量时,其他任务不能再解锁或持有互斥量。互斥控制块:互斥控制块资源由内核创建和维护,内核初始化时调用函数OsMuxInit()初始化锁资源。等待互斥量的任务将挂载在muxList中。typedefstruct{UINT8muxStat;/**<状态OS_MUX_UNUSED,OS_MUX_USED*/UINT16muxCount;/**<锁定互斥量的次数*/UINT32muxID;/**<句柄ID*/LOS_DL_LISTmuxList;/**<互斥链表*/LosTaskCB*owner;/**<正在锁定互斥量的当前线程*/UINT16priority;/**<锁定互斥量的线程的优先级*/}LosMuxCB;内核在初始化时会申请LOSCFG\_BASE\_IPC\_MUX\_LIMIT锁资源,并将每个资源块挂载到双向环链表g\_unusedMuxList中,全局变量g\_allMux指向锁资源内存的首地址,然后根据首地址加ID方法快速找到对应的控件Block:互斥体创建:任务调用LOS\_MuxCreate()创建互斥体,内核从g_unusedMuxList头部分配一个锁资源给任务。互斥锁申请:任务调用LOS\_MuxPend()申请互斥锁。如果锁被其他任务持有,则该任务在muxList上排队。互斥锁释放:任务调用LOS\_MuxPost()释放互斥锁。如果有其他任务在排队,则会触发调度释放对排队任务的锁。互斥锁删除:任务调用LOS\_MuxDelete()来删除互斥锁。如果删除成功,锁资源返回给g\_unusedMuxList。5.数据结构——信号量信号量是一种同步机制,实现任务间的同步或对关键资源的互斥访问。它通常用于协助一组相互竞争的任务访问关键资源。在多任务系统中,每个任务都需要同步或互斥来保护关键资源,信号量功能可以为用户提供这方面的支持。通常,信号量的计数值用来对应有效资源数,表示剩余的可占用互斥量资源数。信号量控制块:信号量控制块资源由内核创建和维护,内核初始化时调用函数OsSemInit()初始化信号量资源。初始化时申请LOSCFG\_BASE\_IPC\_SEM\_LIMIT信号量控制块,g\_allSem指向信号量控制块首地址,创建的信号量控制块会挂载到空闲链表g\_unusedSemList。申请信号量的任务会在控制块的链表semList上排队,semCount表示可以访问的资源个数。typedefstruct{UINT16semStat;/**<信号量状态*/UINT16semCount;/**<可用信号量的数量*/UINT16maxSemCount;/**<可用信号量的最大数量*/UINT16semID;/**<信号量控制结构ID*/LOS_DL_LISTsemList;/**<等待信号量的任务队列*/}LosSemCB;信号量创建:任务调用LOS\_SemCreate()创建信号量,并指定同时访问该资源的最大任务数。内核从g\_unusedSemList的头部分配一个信号量控制块并对其进行初始化。信号量申请:任务调用LOS\_MuxPend()申请信号量。如果有可以访问的资源,则申请成功;否则,它在semList上排队等候。信号量释放:任务调用LOS\_SemPost()释放信号量。如果有其他任务在排队,则触发调度,允许排队任务访问资源。信号量删除:任务调用LOS\_SemDelete()删除信号量。如果删除成功,锁资源返回到g\_unusedSemList的头部。6.小结本文通过队列、事件、互斥量、信号量四个方面的数据结构来分析内核IPC机制的数据结构。希望以上的讲解可以让大家对IPC机制有一个整体的认识。关于OpenHarmony内核的内容,我也介绍了内核对象队列的算法和OpenHarmonyLiteOS-M内核事件的运行机制。感兴趣的读者可以点击阅读:《OpenHarmony——内核对象队列之算法详解(上)》、《OpenHarmony——内核对象队列之算法详解(下)》、《OpenHarmony——内核对象事件之源码详解》。纸上谈兵的成果总是肤浅的,我知道这件事必须要做。要将所有知识转化为能力,我们必须实践。希望所有热爱OpenHarmony的开发者,在今后的开发工作中,学真知真,领悟真谛,加强修养,增长本领,为OpenHarmony生态的繁荣发展不断前行!
