OpenHarmony 实战开发——内核对象队列之算法详解

前言

OpenAtom OpenHarmony(以下简称“OpenHarmony”) LiteOS-M 内核是面向 IoT 领域构建的轻量级物联网操作系统内核,具有小体积、低功耗、高性能的特点。在嵌入式领域的开发工作中,无论是自研还是移植系统,均绕不开内核,开发者只有掌握内核的相关知识,才能更好地深耕物联网产品领域。OpenHarmony LiteOS-M内核对象队列的算法包括FIFO和FILO,在上一期发布的《OpenHarmony-内核对象队列之算法详解(上)》文章中,我分享了OpenHarmonyLiteOS-M内核对象队列的FIFO的算法,今天给大家介绍另外一种算法——FILO算法。

关键数据结构

首先关注队列的关键数据结构LosQueueCB,有了这个数据,才能理解队列是如何工作的:

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; /*< Pointer to the memory linked list /
  }LosQueueCB;

queue:指向消息节点内存区域,创建队列时按照消息节点个数乘每个节点大小从动态内存池中申请一片空间。

queueState:队列状态,表明队列控制块是否被使用,有OS_QUEUE_INUSED和OS_QUEUE_UNUSED两种状态。

queueLen:消息节点个数,表示该消息队列最大可存储多少个消息。

queueSize:每个消息节点大小,表示队列每个消息可存储信息的大小。

queueID:消息ID,通过它来操作队列。

消息节点按照循环队列的方式访问,队列中的每个节点以数组下标表示,下面的成员与消息节点循环队列有关:

queueHead:循环队列的头部。

queueTail:循环队列的尾部。

readWriteableCnt[OS_QUEUE_WRITE]:消息节点循环队列中可写的消息个数,为0表示循环队列为满,等于queueLen表示循环队列为空。

readWriteableCnt[OS_QUEUE_READ]:消息节点循环队列中可读的消息个数,为0表示循环队列为空,等于queueLen表示消息队列为满。 readWriteList[OS_QUEUE_WRITE]:写消息阻塞链表,链接因消息队列满而无法写入时需要挂起的TASK。

readWriteList[OS_QUEUE_READ]:读消息阻塞链表,链接因消息队列空而无法读取时需要挂起的TASK。

memList:申请内存块阻塞链表,链接因申请某一静态内存池中的内存块失败而需要挂起的TASK。

关键算法

在计算机程序设计中,“先入先出”和“先入后出”都是处理输入数据的方法。上篇文章向大家介绍了FIFO(先入先出)算法,今天给大家讲解FILO(先入后出)算法。一个先入后出(FILO,First In Last Out)的队列,可以形象地理解为手枪的弹匣,装子弹是“入队列”,射击是“出队列”,最先压入弹匣的子弹是最后射出去的。同理,最先入队列的消息也是在最后处理,这就是FILO(先入后出)算法的本质。

1.1FIFO算法之入队列

第一步:队列初始化

下图呈现了一个初始化后的队列:

截取关键函数LOS_QueueCreate,此函数来源于liteos_m内核代码。

LITE_OS_SEC_TEXT_INIT UINT32 LOS_QueueCreate(CHAR *queueName,
                                         UINT16 len,
                                         UINT32 *queueID,
                                         UINT32 flags,
                                         UINT16 maxMsgSize)
{
    LosQueueCB *queueCB = NULL;
    UINT32 intSave;
    LOS_DL_LIST *unusedQueue = NULL;
    UINT8 *queue = NULL;
    UINT16 msgSize;
    ...
    queue = (UINT8 )LOS_MemAlloc(m_aucSysMem0, len  msgSize);
    ...
    queueCB->queueLen = len;
    queueCB->queueSize = msgSize;
    queueCB->queue = queue;
    queueCB->queueState = OS_QUEUE_INUSED;
    queueCB->readWriteableCnt[OS_QUEUE_READ] = 0;
    queueCB->readWriteableCnt[OS_QUEUE_WRITE] = len;
    queueCB->queueHead = 0;
    queueCB->queueTail = 0;
    LOS_ListInit(&queueCB->readWriteList[OS_QUEUE_READ]);
    LOS_ListInit(&queueCB->readWriteList[OS_QUEUE_WRITE]);
    LOS_ListInit(&queueCB->memList);
    LOS_IntRestore(intSave);
 
    *queueID = queueCB->queueID;
 
    OsHookCall(LOS_HOOK_TYPE_QUEUE_CREATE, queueCB);
 
    return

queue指针指向队列的内存,队列分配了len个消息,每个消息的大小为msgSize。与此同时头指针和尾指针的初始化为0,意味着队列为空,还没有消息入队列。

第二步:第一个消息入队列

各类任务可以作为队列的生产者,队列初始化后,任务可以放置第一个消息,在此选择FILO的方式来放置消息。

下图是FIFO插入第一个数据后的内存形态:

FILO的操作包含在OsQueueBufferOperate函数中,这次是进入OS_QUEUE_WRITE_HEAD的分支处理:

static INLINE VOID OsQueueBufferOperate(LosQueueCB *queueCB, UINT32 operateType,
                                                            VOID bufferAddr, UINT32 bufferSize)
{
    UINT8 *queueNode = NULL;
    UINT32 msgDataSize;
    UINT16 queuePosition;
    errno_t rc;
 
    / get the queue position /
    switch (OS_QUEUE_OPERATE_GET(operateType)) {
        case OS_QUEUE_READ_HEAD:
            queuePosition = queueCB->queueHead;
            ((queueCB->queueHead + 1) == queueCB->queueLen) ? (queueCB->queueHead = 0) : (queueCB->queueHead++);
            break;
 
        case OS_QUEUE_WRITE_HEAD:
            (queueCB->queueHead == 0) ? (queueCB->queueHead = (queueCB->queueLen - 1)) : (--queueCB->queueHead);
            queuePosition = queueCB->queueHead;
            break;
 
        case OS_QUEUE_WRITE_TAIL:
            queuePosition = queueCB->queueTail;
            ((queueCB->queueTail + 1) == queueCB->queueLen) ? (queueCB->queueTail = 0) : (queueCB->queueTail++);
            break;
    ...
}

OsQueueBufferOperate是队列内存的核心操作函数,FILO算法的本质是往队列的头部添加数据,入队列的操作抽象为OS_QUEUE_WRITE_HEAD操作。而本次操作和FIFO不一样,插入数据不再移动tail这个“尾巴”指针,后续无论是入队列操作还是出队列操作,tail指针都不会被操作。

第三步:继续生产数据

数据继续生产,第2个消息进入队列后继续移动head指针,如下图所示:

第三个消息也是重复的移动head指针,如下图所示:

第四步:生产数据结束

本次实例以生产者生产四个消息为结束点,最后形态的队列下图所示:

1.2 FIFO算法之出队列

第一步:取出队列头消息。由于这是先入后出的算法,因此第一个出队列的消息是最后入队列的,也就是队列中标注为“第4个”的消息。

消费后的消息空间也是unused空间,在此处用其它颜色标注消费后的消息,便于读者理解队列的变化情况。

回顾一下OsQueueBufferOperate函数的关键代码,这一次是读的分支:

/ get the queue position /
switch (OS_QUEUE_OPERATE_GET(operateType)) {
    case OS_QUEUE_READ_HEAD:
        queuePosition = queueCB->queueHead;
        ((queueCB->queueHead + 1) == queueCB->queueLen) ? (queueCB->queueHead = 0) : (queueCB->queueHead++);
        break;

queueHead是头指针,它的移动代表着出队列的行为,queueHead目前指向“第4个”消息,往后移动一个,应用得到“第4个”消息的返回值。此处可见,最后入队列的消息最先出。

第二步:继续消费

第三个消息被消费的图示:

第二个消息被消费的图示:

第三步:消费完毕

最后一个消息也处理完成,于是head指针和tail指针均移动到下图的位置。队列为空,任务结束。

这时如果把图重新换个方向来看,那么就很容易了解这个算法的本质。Tail指针全程没有用到,如果把它去掉,水平方向的队列改为垂直方向。如下图所示,可见该图片为典型的入栈操作。由此可知,OpenHarmony内核通过头指针的写操作和读操作,把栈的操作兼容到队列中。

总结

本文主要介绍了OpenHarmony内核对象队列的算法之FILO,至此,队列的2个算法都已介绍完毕。通过对FIFO和FILO这2个算法的详解,开发者能够更加全面了解OpenHarmony LiteOS-M 内核队列算法,以便将来在内核开发工作中遇到队列的其他算法,也能够举一反三,迅速掌握。

为了帮助到大家能够更有效的学习OpenHarmony 开发的内容,下面特别准备了一些相关的参考学习资料:

OpenHarmony 开发环境搭建:https://qr18.cn/CgxrRy

《OpenHarmony源码解析》:https://qr18.cn/CgxrRy

  • 搭建开发环境
  • Windows 开发环境的搭建
  • Ubuntu 开发环境搭建
  • Linux 与 Windows 之间的文件共享
  • ……

系统架构分析:https://qr18.cn/CgxrRy

  • 构建子系统
  • 启动流程
  • 子系统
  • 分布式任务调度子系统
  • 分布式通信子系统
  • 驱动子系统
  • ……

OpenHarmony 设备开发学习手册:https://qr18.cn/CgxrRy

在这里插入图片描述

OpenHarmony面试题(内含参考答案):https://qr18.cn/CgxrRy

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/655282.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

超越中心化:Web3的去中心化应用探索

随着区块链技术的迅速发展&#xff0c;Web3作为其最前沿的应用之一&#xff0c;正引领着互联网进入了一个新的时代。Web3不仅仅是技术的进步&#xff0c;更是一种全新的思维方式和社会模式&#xff0c;其核心理念是去中心化、自治和透明&#xff0c;这与传统的中心化互联网模式…

视创云展「VR直播」是什么?有哪些功能和应用场景?

视创云展「VR直播」通过“3D沉浸式展厅直播高互动感”的创新玩法&#xff0c;使企业随时随地举办一场低成本、高互动、能获客的元宇宙直播活动成为可能。「VR直播」能实现3D展厅内VR场景漫游&#xff0c;更结合音视频交互、同屏互动等新功能&#xff0c;为用户带来更沉浸的虚拟…

.NET周刊【5月第4期 2024-05-26】

国内文章 开源低代码框架 ReZero API 正式版本发布 &#xff0c;界面操作直接生成API https://www.cnblogs.com/sunkaixuan/p/18201175 ReZero是一款.NET6的中间件&#xff0c;采用MIT许可证开源&#xff0c;目的是降低.NET Core开发的门槛。它提供界面操作生成API的功能&am…

nacos安装与使用

1.nacos简介与安装 什么是注册中心&#xff08;服务治理&#xff09; 服务注册&#xff1a;服务提供者provider&#xff0c;启动的时候向注册中心上报自己的网络信息 服务发现&#xff1a;服务消费者consumer&#xff0c;启动的时候向注册中心上报自己的网络信息&#xff0c;拉…

《C++ Primer Plus》第十二章复习题和编程练习

目录 一、复习题二、编程练习 一、复习题 1. 假设String类有如下私有成员&#xff1a; // String 类声明 class String { private: char* str;int len;// ... };a. 下述默认构造函数有什么问题&#xff1f; String::String() { } // 默认构造函数b. 下述构造函数有什么问题…

民国漫画杂志《时代漫画》第29期.PDF

时代漫画29.PDF: https://url03.ctfile.com/f/1779803-1248635405-bf3c87?p9586 (访问密码: 9586) 《时代漫画》的杂志在1934年诞生了&#xff0c;截止1937年6月战争来临被迫停刊共发行了39期。 ps: 资源来源网络!

分享之远程调试

1:在线上启动脚本添加如下的内容&#xff1a; #! /bin/sh# 设置启动的jar SERVICE_NAME"xxx.jar"PRJ_BIN_DIR$(dirname $(readlink -f "$0")) SERVICE_HOME$(dirname $PRJ_BIN_DIR)LOGS_DIR$SERVICE_HOME/logs # 控制台日志 STDOUT_FILE$SERVICE_HOME/log…

New Phytologist:杨树特有miRNA在调控杨树抗旱中的分子机制

2024年3月6日&#xff0c;林木遗传育种全国重点实验室、北京林业大学生物科学与技术学院尹伟伦与夏新莉教授课题组在New Phytologist&#xff08;中科院一区&#xff0c;影响因子9.4&#xff09;期刊发表了题为“The miR6445-NAC029 module regulates drought tolerance by reg…

Python3 笔记:Python的所有关键字

查看Python的关键字首先需要用import导入keyword模块 import keyword # 查看Python的所有关键字&#xff0c;先用import导入keyword模块 print(keyword.kwlist) 运行结果&#xff1a; [False, None, True, and, as, assert, async, await, break, class, continue, def, …

96.网络游戏逆向分析与漏洞攻防-ui界面的设计-角色管理功能的界面设计

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果&#xff0c;代码看不懂是正常的&#xff0c;只要会抄就行&#xff0c;抄着抄着就能懂了 内容…

QT使用gsoap获取手机归属地

1-环境变量 用的win32 E:\hes_scc\tools\gsoap_2.8.134\gsoap-2.8\gsoap\bin\win32 2-生成代码接口 自己建一个目录&#xff0c;在此打开cmd窗口&#xff0c;生成的文件都会在这个文件夹中。 这里用的手机归宿地。 wsdl2h -o GetPhoneInfo.h -s -n Phone -t ....\typemap.…

DES加密算法笔记

【DES加密算法&#xff5c;密码学&#xff5c;信息安全】https://www.bilibili.com/video/BV1KQ4y127AT?vd_source7ad69e0c2be65c96d9584e19b0202113 根据此视频学习 DES是对称密码中的分组加密算法 (分组加密对应流加密算法) 流加密算法就是一个字节一个字节加密 分组加…

SSL协议:网络安全通信的守护者

在网络通信迅猛发展的今天&#xff0c;数据安全和隐私保护变得尤为重要。安全套接层协议&#xff08;Secure Sockets Layer, SSL&#xff09;作为早期网络加密及身份验证的基石&#xff0c;为在线数据传输提供了安全保障。下面我们就来了解一下SSL协议。 SSL协议概述 SSL协议最…

NSSCTF | [SWPUCTF 2021 新生赛]no_wakeup

打开题目后&#xff0c;点击三个&#xff1f;&#xff0c;发现是一个php序列化脚本 <?phpheader("Content-type:text/html;charsetutf-8"); error_reporting(0); show_source("class.php");class HaHaHa{public $admin;public $passwd;public function…

System32文件夹千万不能删除,看完这篇你就知道为什么了

序言 C:\Windows\System32目录是Windows操作系统的关键部分,重要的系统文件存储在该目录中。网上的一些恶作剧者可能会告诉你删除它,但你不应该尝试去操作,如果你尝试的话,我们会告诉你会发生什么。 什么是System32文件夹 位于C:\Windows\System32的System32文件夹是所有…

OpenHarmony应用开启Service以及完成自启动和常驻

一.背景 由于有需求实现一个后台常驻服务,这里就是来实现在鸿蒙里面如何实现后台服务并且实现自启动和常驻 二.添加服务 如下来添加服务 然后此时直接运行这个hap是报错的,如下: 此处参考: 应用中添加ServiceExtensionAbility然后安装HAP时提示“code:9568344 error: inst…

计算机二级Access操作题总结——基本操作

基础操作题 设置主键 例&#xff1a;将“线路”表中的“线路ID”字段设置为主键 ①右键单击“线路”表&#xff1b; ②单击【设计视图】&#xff1b; ③鼠标指到表的第一行→“线路ID”处&#xff0c;右键单击&#xff1b; ④单击【主键】 设置有效性规则 例&#xff1a;设…

算法之背包问题

可分的背包问题是可以用贪心法来解决&#xff0c;而0-1背包问题通常使用动态规划方法来解决。 可分背包问题&#xff1a; 在可分背包问题中&#xff0c;物品可以被分割&#xff0c;您可以取走物品的一部分以适应背包的容量。这里的关键是物品的价值密度&#xff0c;即单…

【电路笔记】-二阶滤波器

二阶滤波器 二阶(或双极)滤波器由两个连接在一起的 RC 滤波器部分组成,可提供 -40dB/十倍频程滚降率。 1、概述 二阶滤波器也称为 VCVS 滤波器,因为运算放大器用作压控电压源放大器,是有源滤波器设计的另一种重要类型,因为与我们之前研究过的有源一阶 RC 滤波器一起,…

常见排序算法之选择排序

目录 一、选择排序 1.1 什么是选择排序&#xff1f; 1.2 思路 1.2.1 思路一 1.2.2 优化思路 1.3 C语言源码 1.3.1 思路一 1.3.2 优化思路 二、堆排序 2.1 调整算法 2.1.2 向上调整算法 2.1.3 向下调整算法 2.2 建堆排序 一、选择排序 1.1 什么是选择排序&#xf…