一、前言

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生态的繁荣发展不断前行!

OpenHarmony——内核IPC机制数据结构解析的更多相关文章

  1. Android IPC机制全解析<一>

    概要 多进程概念及多进程常见注意事项 IPC基础:Android序列化和Binder 跨进程常见的几种通信方式:Bundle通过Intent传递数据,文件共享,ContentProvider,基于Bi ...

  2. Handler消息机制与Binder IPC机制完全解析

    1.Handler消息机制 序列 文章 0 Android消息机制-Handler(framework篇) 1 Android消息机制-Handler(native篇) 2 Android消息机制-H ...

  3. 内核ipc机制

    内核版本:linux2.6.22.6 硬件平台:JZ2440 驱动源码 block_ipc_poll_key_int_drv.c : #include <linux/module.h> # ...

  4. Android IPC机制全解析<二>

    在AIDL文件中并不是所有的数据类型都可以使用,AIDL支持的数据类型如下: 基本数据类型(int.long.char.boolean.double等) String和CharSequence Lis ...

  5. 【内核】Linux内核Initrd机制解析,内核更新步骤,grub配置说明

    什么是Initrd initrd的英文含义是 boot loader initialized RAM disk,就是由boot loader初始化的内存盘.在 linux内核启动前, boot loa ...

  6. [置顶] 深入理解android之IPC机制与Binder框架

    [android之IPC机制与Binder框架] [Binder框架.Parcel.Proxy-Stub以及AIDL] Abstract [每个平台都会有自己一套跨进程的IPC机制,让不同进程里的两个 ...

  7. Anciroid的IPC机制-Binder原理

    Binder驱动的原理和实现 通过上一节的介绍,大家应该对Binder有了基本的认识了.任何上层应用程序接口和用户操作都需要底层硬件设备驱动的支持,并为其提供各种操作接口.本节首先从Binder的驱动 ...

  8. Android之IPC机制

    Android IPC简介 任何一个操作系统都需要有相应的IPC机制,Linux上可以通过命名通道.共享内存.信号量等来进行进程间通信.Android系统不仅可以使用了Binder机制来实现IPC,还 ...

  9. Linux内核同步机制--转发自蜗窝科技

    Linux内核同步机制之(一):原子操作 http://www.wowotech.net/linux_kenrel/atomic.html 一.源由 我们的程序逻辑经常遇到这样的操作序列: 1.读一个 ...

  10. Linux内核同步机制

    http://blog.csdn.net/bullbat/article/details/7376424 Linux内核同步控制方法有很多,信号量.锁.原子量.RCU等等,不同的实现方法应用于不同的环 ...

随机推荐

  1. maketrans和translate按规则一次性替换多个字符,用来替代replace

    str_ = 'i love you' compiler_ = str_.maketrans('i l y', 'I L Y') print(str_.translate(compiler_))

  2. 6大数据类型之间的转换及数据在内存中的缓存机制----day03

    1.自动类型转换 当2个不同类型的数据进行运算的时候,默认向更高精度转换 数据类型精度从低到高:bool  < int < float <complex 1,1强制类型转换 # Nu ...

  3. java基础之StringBuilder---03

    StringBuilder概述 StringBuilder是一个可变的字符串类,我们可以把它看成是一个容器,这里的可变指的是StringBuilder对象中的内容是可变的. 如果对字符串进行拼接操作, ...

  4. React之父组件向子组件传值

    class Parent extends React.Component{ constructor(){ super(); this.state={co:"red"} } rend ...

  5. 云原生:使用HPA和VPA实现集群扩缩容

    1 背景 我们之前介绍过,随着业务流量上涨之后,我们的系统需要适时的进行扩容. 数据存储层我们也介绍过MySQL的扩容 Scale UP(纵向扩展) 和 Scale Out(横向扩展) 垂直拆分(Sc ...

  6. 【Azure Redis 缓存】Azure Redis读写比较慢/卡的问题排查

    问题描述 在使用Azure Redis的过程中发现读写比较慢,非常卡,执行扩容6-->13GB后,过一段时间也满了.在通过门户Console连接到Reids,通过info Memory名称查看到 ...

  7. 【Azure 事件中心】关闭或开启Azure Event Hub SDK中的日志输出

    问题描述 使用Azure Event Hub的Java SDK 作为消费端消费消息,集成在项目中后,发现大量日志产生,并且都是Debug 级别日志,如何来关闭这部分日志输出呢? import com. ...

  8. 【Azure 存储服务】App Service 访问开启防火墙的存储账号时遇见 403 (This request is not authorized to perform this operation.)

    问题描述 需要 App Service 访问开启防火墙的存储账号.存储账号中设置为允许选中的VNET访问,同时允许了信任的Azure服务的访问,但是仍然报错 "403 (This reque ...

  9. 俄罗斯套娃 (Matryoshka) 嵌入模型概述

    在这篇博客中,我们将向你介绍俄罗斯套娃嵌入的概念,并解释为什么它们很有用.我们将讨论这些模型在理论上是如何训练的,以及你如何使用 Sentence Transformers 来训练它们. 除此之外,我 ...

  10. Vue3 写业务逻辑不适合用TS(TypeScript)

    TypeScript 最重要的就是装饰器 Vue3最重要更新就是Setup 装饰器就是为了打散功能点,Vue3的Setup功能也是打散功能点,那用Vue3上TS,感觉就是自己给自己多一层工作量. 我这 ...