共计 4098 个字符,预计需要花费 11 分钟才能阅读完成。
一、前言
OpenAtom OpenHarmony(以下简称“OpenHarmony”)是由凋谢原子开源基金会(OpenAtom Foundation)孵化及经营的开源我的项目,指标是面向全场景、全连贯、全智能时代,基于开源的形式,搭建一个智能终端设备操作系统的框架和平台,促成万物互联产业的凋敝倒退。
作为面向全场景、全连贯、全智能的分布式泛终端操作系统,OpenHarmony 通过将各类不同终端设备的能力进行整合,实现硬件互助、资源共享,为用户提供晦涩的全场景体验。为了能适应各种硬件,OpenHarmony 提供了 LiteOS、Linux 内核,并基于这些内核造成了不同的零碎类型,同时又在这些零碎中构建了一套对立的零碎能力。
OpenHarmony LiteOS- M 内核是面向 IoT 畛域构建的轻量级物联网操作系统内核,LiteOS- M 核为工作间通信提供了多种机制,包含队列、事件、互斥锁和信号量。各机制波及到哪些要害数据结构?这些数据结构又是如何工作的?接下来我将从队列、事件、互斥锁、信号量几个内核对象登程,为大家解说内核 IPC 机制的数据结构。
二、数据结构 – 队列
队列又称音讯队列,是一种罕用于工作间通信的数据结构,能够在工作间传递音讯内容或音讯的地址。内核用队列管制块来治理音讯队列,同时又应用双向环形链表来管理控制块。
队列管制块:治理具体音讯队列的数据块,内核初始化时调用 OsQueueInit()创立,并顺次挂载到双向环形链表 g_freeQueueList 中,此时管制块状态为 OS\_QUEUE\_UNUSED,队列管制块用来保留队列的状态,队列长度、音讯长度、队列 ID、队列头尾地位和期待读写的工作列表,内核就是依据这些信息来治理音讯队列和工作实现对音讯读写等操作。
typedef struct { | |
UINT8 *queue; | |
UINT16 queueState; | |
UINT16 queueLen; | |
UINT16 queueSize; | |
UINT16 queueID; | |
UINT16 queueHead; | |
UINT16 queueTail; | |
UINT16 readWriteableCnt[OS_READWRITE_LEN]; | |
LOS_DL_LIST readWriteList[OS_READWRITE_LEN]; | |
LOS_DL_LIST memList; | |
}LosQueueCB; |
初始化后队列管制块的组织形式如下:
创立队列:队列用于寄存具体的音讯内容,工作能够调用 LOS\_QueueCreate()来创立队列,此时内核会依据入参指定的队列长度和音讯大小申请内存创立队列,并从 g\_freeQueueList 中调配一个管制块来治理队列,被调配的队列管制块状态为 OS\_QUEUE\_INUSED。调配队列管制块时总是从头节点开始,如下图管制块0首先被调配用于治理新创建的队列。
写队列:内核反对两种写队列形式:从尾部写入 LOS\_QueueWrite()和 从头部写入 LOS\_QueueWriteHead():
读队列:读队列只有一种形式,从队列头部读 LOS\_QueueRead(),读取之后 head 指向下个节点。
删除队列:当不再应用队列时能够应用 LOS\_QueueDelete()来删除队列,此时会偿还队列管制块到 g_freeQueueList 中,并开释音讯队列:
三、数据结构 – 事件
事件用于实现工作间的同步,但事件通信只能是事件类型的通信,无数据传输,事件管制块由工作申请,内核负责保护。事件管制块:事件管制块用来记录事件和治理期待读取事件的工作。uwEventID 总共 32bit 代表 31 个事件(bit25 保留),stEventList 事件管制块的双向环形链表,当有工作读取事件但事件还没产生时工作会被挂载链表中,当事件产生时零碎唤醒期待事件的工作,此时工作就会被摘出链表。
typedef struct tagEvent { | |
UINT32 uwEventID; | |
LOS_DL_LIST stEventList; | |
} EVENT_CB_S, *PEVENT_CB_S; |
事件初始化 :事件管制块由工作创立,而后调用 LOS_EventInit() 进行初始化,初始化后的状态如下:
事件读:当事件没有产生时,读事件操作会引发系统调度,把当前任务挂起并退出到 stEventList 链表,下图中事件 1 产生,工作 Task1 读取事件 2,然而事件 2 没有产生导致 Task1 被挂起。
事件写:当事件 2 产生时,工作 Task2 把事件 2 写入 uwEventID,此时工作 Task1 被调度读取事件胜利,事件 2 对应 bit 位被清 0(也能够不清 0),Task1 从链表 stEventList 中被摘出。
事件删除:事件管制块是由工作创立的,内核不负责管制块的删除,然而工作能够调用 LOS\_EventClear 来革除事件。
四、数据结构 – 互斥锁
互斥锁又称互斥型信号量,是一种非凡的二值性信号量,用于实现对共享资源的独占式解决。任意时刻互斥锁的状态只有开锁或闭锁,当有工作持有时,互斥锁处于闭锁状态,工作取得该互斥锁的所有权;当该工作开释它时,互斥锁被开锁,工作失去该互斥锁的所有权;当一个工作持有互斥锁时,其余工作将不能再对该互斥锁进行开锁或持有。互斥锁管制块:互斥锁管制块资源由内核创立和保护,内核初始化时会调用函数 OsMuxInit()对锁资源进行初始化。期待互斥锁的工作会被挂载到 muxList 中。
typedef struct { | |
UINT8 muxStat; /**< State OS_MUX_UNUSED,OS_MUX_USED */ | |
UINT16 muxCount; /**< Times of locking a mutex */ | |
UINT32 muxID; /**< Handle ID */ | |
LOS_DL_LIST muxList; /**< Mutex linked list */ | |
LosTaskCB *owner; /**< The current thread that is locking a mutex */ | |
UINT16 priority; /**< Priority of the thread that is locking a mutex */ | |
} LosMuxCB; |
初始化时内核会申请 LOSCFG\_BASE\_IPC\_MUX\_LIMIT 个锁资源,并把各资源块挂载到双向环形链表 g\_unusedMuxList 中,全局变量 g\_allMux 指向锁资源内存首地址,后续依据首地址加 ID 形式疾速查找对应的管制块:
互斥锁创立 :工作调用 LOS\_MuxCreate() 创立互斥锁,内核会从 g_unusedMuxList 的头部调配一个锁资源给工作。
互斥锁申请 :工作调用 LOS\_MuxPend() 申请互斥锁,如果锁被其它工作持有,则工作在 muxList 上排队。
互斥锁开释 :工作调用 LOS\_MuxPost() 开释互斥锁,如果有其它工作排队,则触发调度开释锁给排队工作。
互斥锁删除 :工作调用 LOS\_MuxDelete() 删除互斥锁,如果删除胜利锁资源被偿还到 g\_unusedMuxList 中。
五、数据结构 – 信号量
信号量实现工作之间同步或临界资源的互斥拜访的一种同步机制,罕用于帮助一组相互竞争的工作来拜访临界资源。在多任务零碎中,各工作之间须要同步或互斥实现临界资源的爱护,信号量性能能够为用户提供这方面的反对。通常一个信号量的计数值用于对应无效的资源数,示意剩下的可被占用的互斥资源数。信号量管制块:信号量管制块资源由内核创立和保护,内核初始化时会调用函数 OsSemInit()对信号量资源进行初始化。初始化时申请 LOSCFG\_BASE\_IPC\_SEM\_LIMIT 个信号量管制块,g\_allSem 指向信号量管制块的首地址,创立好的信号量管制块会挂载到闲暇链表 g\_unusedSemList 中。申请信号量的工作会在管制块的链表 semList 上排队,semCount 批示能够被拜访的资源数。
typedef struct { | |
UINT16 semStat; /**< Semaphore state */ | |
UINT16 semCount; /**< Number of available semaphores */ | |
UINT16 maxSemCount; /**< Max number of available semaphores */ | |
UINT16 semID; /**< Semaphore control structure ID */ | |
LOS_DL_LIST semList; /**< Queue of tasks that are waiting on a semaphore */ | |
} LosSemCB; |
信号量创立 :工作调用 LOS\_SemCreate() 创立信号量,并指定同一时刻拜访此资源的最大工作数目。内核从 g\_unusedSemList 的头部调配一个信号量管制块并初始化。
信号量申请 :工作调用 LOS\_MuxPend() 申请信号量,如果有资源能够拜访则申请胜利,否则在 semList 上排队等待。
信号量开释 :工作调用 LOS\_SemPost() 开释信号量,如果有其它工作排队,则触发调度使排队工作拜访资源。
信号量删除 :工作调用 LOS\_SemDelete() 删除信号量,如果删除胜利,锁资源被偿还到 g\_unusedSemList 的头部。
六、总结
本篇文章通过数据结构的队列、事件、互斥锁、信号量四大方面对内核 IPC 机制数据结构进行解析,心愿以上的解说能给大家建设一个 IPC 机制的整体意识。对于 OpenHarmony 内核的内容,之前我还介绍了内核对象队列的算法以及 OpenHarmony LiteOS-M 内核事件的运作机制,感兴趣的读者能够点击浏览:《OpenHarmony——内核对象队列之算法详解(上)》、《OpenHarmony——内核对象队列之算法详解(下)》、《OpenHarmony——内核对象事件之源码详解》。纸上得来终觉浅,绝知此事要躬行。所有常识转化为能力,都必须躬身实际,愿所有酷爱 OpenHarmony 的开发者,在将来的开发工作中学真知、悟真谛,增强磨炼、增长本事,为 OpenHarmony 生态的凋敝倒退一直前行!