OpenHarmony智能开发套件—内核编程(上)

本篇主要介绍了一些基础内核编程相关的内容,希望能够帮助到学习OpenHarmony的伙伴们。
首页 新闻资讯 行业资讯 OpenHarmony智能开发套件—内核编程(上)

7822686224c65926639457b2343e1ee340b6b9.png

想了解更多关于开源的内容,请访问:

51CTO 开源基础软件社区

https://ost.51cto.com

前言

本篇具体介绍OpenHarmony在智能开发套件Hi3861上的内核编程学习。

编程入门[Hello,OpenHarmony]

在正式开始之前,对于刚接触OpenHarmony的伙伴们,面对大篇幅的源码可能无从下手,不知道怎么去编码写程序,下面用一个简单的例子带伙伴们入门。

任务

编写程序,让开发板在串口调试工具中输出”Hello,OpenHarmony“。

操作

在源码的根目录中有名为”applications“的文件,他存放着应用程序样例,下面是他的目录结构:

OpenHarmony智能开发套件[内核编程·上]-开源基础软件社区

我们要编写的程序样例就在源码根目录下的:applications/sample/wifi-iot/app/。

下面将具体演示如何编写程序样例。

  1. 新建样例目录
    applications/sample/wifi-iot/app/hello_demo

  2. 新建源文件和gn文件
    applications/sample/wifi-iot/app/hello_demo/hello.c
    applications/sample/wifi-iot/app/hello_demo/BUILD.gn

OpenHarmony智能开发套件[内核编程·上]-开源基础软件社区

  1. 编写源文件

hello.c:

#include<stdio.h>#include"ohos_init.h"voidhello(void){printf("Hello,OpenHarmony!");}SYS_RUN(hello);

第一次操作的伙伴们可能会在引入”ohos_init.h“库时报错,面对这个问题我们只需要修改我们的include path即可,一般我们直接在目录下的 .vscode/c_cpp_properties.json文件中直接修改includePath

OpenHarmony智能开发套件[内核编程·上]-开源基础软件社区

笔者的代码版本是OpenHarmony3.2Release版,不同版本的源码可能库所存放的路径不同,那么怎么去找到对应的库呢,对于不熟悉源码结构的伙伴们学习起来很不友好。

对于在纯Windows环境开发的伙伴们,笔者推荐使用everything这款工具,它可以快速查找主机中的文件,比在资源管理器的搜索快上不少。

OpenHarmony智能开发套件[内核编程·上]-开源基础软件社区

everything官网

everything似乎不能找到我WSL中的Ubuntu中的文件,因此对于Windows + Linux环境下的伙伴们,这款工具又不那么适用。那就可以根据Linux的查询指令来定位文件所在目录,下面提供查询案例防止有不熟悉Linux的伙伴们。我们使用locate指令来查找文件。

首先安装locate。

sudo apt install mlocate

更新mlocate.db。

sudo updatedb

查询文件目录。

locate ohos_init.h

找到我们源码根目录下 include路径下的ohos_init.h文件。

OpenHarmony智能开发套件[内核编程·上]-开源基础软件社区

  1. 编写gn文件。

static_library("sayHello"){
    sources = [
        "hello.c"
    ]
    include_dirs = [
        "//commonlibrary/utils_lite/include"
    ]
}

static_library表示我们编写的静态模块,名为"sayHello", sources表示我们要编译的源码,include_dirs表示我们引入的库,这里的双斜杠就代表我们的源码根目录,”/commonlibrary/utils_lite/include“就是我们ohos_init.h的所在目录。

  1. 编写app下的gn文件。

在app的目录下也有一个gn文件,我们只需要去修改他即可。

OpenHarmony智能开发套件[内核编程·上]-开源基础软件社区

这表示我们的程序将会执行hello_demo样例中的sayHello模块。

  1. 编译,烧录,串口调试。

这一步就属于基础操作了,不做过多赘述,不会的伙伴们可以看我之前发布的[环境搭建篇],里面也详细介绍了操作流程。

环境搭建篇

  1. 观察控制台的输出。

OpenHarmony智能开发套件[内核编程·上]-开源基础软件社区

至此编码完成了编码入门,下面就具体介绍OpenHarmony的内核编程。

内核

内核介绍

什么是内核?或者说内核在一个操作系统中起到一个什么样的作用?相信初次接触这个词的伙伴们也会有同样的疑问。不过不用担心,笔者会尽可能地通俗地介绍内核的相关知识,以便大家能够更好地去体会内核编程。

我们先来看一张图,这是OpenHarmony官网发布的技术架构图。

OpenHarmony智能开发套件[内核编程·上]-开源基础软件社区

我们可以看到最底层叫做内核层,有Linux,LiteOS等。内核在整个架构,或者操作系统中起到一个核心作用,他负责管理计算机系统内的资源和硬件设备,提供给顶层的应用层一个统一规范的接口,从而使得整个系统能够完成应用与硬件的交互。

具体点来说,内核可以做以下相关的工作:

  1. 进程管理

  2. 内存管理

  3. 文件资源管理

  4. 网络通信管理

  5. 设备驱动管理

当然不局限于这些,这里只是给出具体的例子供伙伴们理解,如果实在难以理解,那么笔者再举一个例子,进程。可能你没听过进程,但你一定打开过任务管理器。

OpenHarmony智能开发套件[内核编程·上]-开源基础软件社区

这些都是进程,一个进程又由多个线程组成。那么CPU,内存,硬盘,网络这些硬件层面资源是怎么合理分配到我们软件的各个进程中呢?这就是内核帮助我们完成的事情,我们并不关心我们设备上的应用在哪里执行,如何分配资源,内核会完成这些事情。我们日常与软件交互,而内核会帮助我们完成软件和硬件的交互。

OpenHarmony内核

明白了什么是内核后,我们来看看OpenHarmony的内核是怎么样设计的吧。

OpenHarmony采用的是多内核设计 有基于Linux内核的标准系统,有基于LiteOS-A的小型系统,也有基于LiteOS-M的轻量系统。他们分别适配不同的设备,比如说智能手表就是轻量级别的,智能汽车就是标准级别的等等。本篇并不介绍标准系统和小型系统,轻量系统更加适合初学者。

LiteOS-M内核

下面是一张LiteOS-M的架构图。

OpenHarmony智能开发套件[内核编程·上]-开源基础软件社区

下面重点介绍KAL抽象层 和 基础内核的操作。

KAL抽象层

相信大家还是会有疑惑,什么是KAL抽象层?

Kernel Abstraction Layer。

在刚刚的内核中我们提到了,内核主要完成的是软件与硬件的交互,他会给应用层提供统一的规范接口,而KAL抽象层正是内核对应用层提供的接口集合。应用程序可以通过KAL抽象层完成对硬件的控制交互。

抽象层是因为他隐藏了与硬件接口具体的交互逻辑,开发人员只需要关心如何操作硬件,而无需关心硬件底层的细节,大大提高了可移植性和维护性。

以笔者的角度去看,KAL简单来说就是一堆接口,帮助你去操控硬件。CMSIS与POSIX就是具有统一规范的一些接口。通过他们我们就可以去控制一些基础的内核,线程,软件定时器,互斥锁,信号量等等。概念就先简单介绍这么多,感兴趣的伙伴们可以上官网查看更多的关于OpenHarmony内核的信息。下面笔者会带着大家编码操作,从实际去体会内核编程。

内核编程

线程管理

在管理线程前,我们需要了解线程,线程是调度的基本单位,具有独立的栈空间和寄存器上下文,相比与进程,他是轻量的。举一个实际的例子,动物园卖票。

对于动物园卖票这件事本身而言是一个进程,而每一个买票的人可以看作一个线程,在多个售票口处,我们并发执行,并行计算,共同消费动物园的门票,像享受共同的内存资源空间一样。为什么要线程管理呢?你我都希望买到票,但是票有限,我们都不希望看到售票厅一篇混乱,因此对线程进行管理是非常重要的一件事情。

任务

创建一个线程,每间隔0.1秒,输出“Hello,OpenHarmony”,1秒后终止线程。

操作

回忆第一个hello.c的例子。

我们要编写的程序样例就在源码根目录下的:applications/sample/wifi-iot/app/。

下面将具体演示如何编写程序样例。

  1. 新建样例目录
    applications/sample/wifi-iot/app/thread_demo。

  2. 新建源文件和gn文件
    applications/sample/wifi-iot/app/thread_demo/singleThread.c
    applications/sample/wifi-iot/app/thread_demo/BUILD.gn

OpenHarmony智能开发套件[内核编程·上]-开源基础软件社区

  1. 编写源码

注意:我们需要使用到cmsis_os2.h这个库,请伙伴们按照笔者介绍的方法把includePath修改好。

问题一:怎么创建线程?

typedef struct {
  /** Thread name */
  const char                   *name;
  /** Thread attribute bits */
  uint32_t                 attr_bits;
  /** Memory for the thread control block */
  void                      *cb_mem;
  /** Size of the memory for the thread control block */
  uint32_t                   cb_size;
  /** Memory for the thread stack */
  void                   *stack_mem;
  /** Size of the thread stack */
  uint32_t                stack_size;
  /** Thread priority */
  osPriority_t              priority;
  /** TrustZone module of the thread */
  TZ_ModuleId_t            tz_module;
  /** Reserved */
  uint32_t                  reserved;
} osThreadAttr_t;

这是线程的结构体,它具有以下属性:

  • name:线程的名称。

  • attr_bits:线程属性位。

  • cb_mem:线程控制块的内存地址。

  • cb_size:线程控制块的内存大小。

  • stack_mem:线程栈的内存地址。

  • stack_size:线程栈的大小。

  • priority:线程的优先级。

  • tz_module:线程所属的TrustZone模块。

  • reserved:保留字段。

问题二:怎么把线程启动起来呢?

osThreadId_tosThreadNew(osThreadFunc_tfunc,void*argument,constosThreadAttr_t*attr);

这是创建线程的接口函数,他有三个参数,一个返回值,我们来逐个解析。

func: 是线程的回调函数,你创建的这个线程会执行这段函数的内容。

arguments:线程回调函数的参数。

attr:线程的属性,也就是我们之前创建的线程

返回值:线程的id 如果id不为空则说明成功。

问题三:怎么终止线程呢?

osStatus_tosThreadTerminate(osThreadId_tthread_id);

显然我们只要传入线程的id就会让该线程终止,返回值是一个状态码,下面给出全部的状态码。

typedefenum{/** Operation completed successfully */osOK=0,/** Unspecified error */osError=-1,/** Timeout */osErrorTimeout=-2,/** Resource error */osErrorResource=-3,/** Incorrect parameter */osErrorParameter=-4,/** Insufficient memory */osErrorNoMemory=-5,/** Service interruption */osErrorISR=-6,/** Reserved. It is used to prevent the compiler from optimizing enumerations. */osStatusReserved=0x7FFFFFFF}osStatus_t;

回调函数怎么写?当然是结合我们的任务,每间隔0.1秒,输出“Hello,OpenHarmony”,1秒后终止。讲到这里,代码的整体逻辑是不是就清晰了很多,直接上完整代码。

#include<stdio.h>#include"ohos_init.h"// CMSIS#include"cmsis_os2.h"// POSIX#include<unistd.h>// 线程回调函数voidprintThread(void*args){(void)args;while(1){printf("Hello,OpenHarmony!\r\n");// 休眠0.1秒osDelay(10);}}voidthreadTest(void){// 创建线程osThreadAttr_tattr;attr.name="mainThread";// 线程attr.cb_mem=NULL;attr.cb_size=0U;attr.stack_mem=NULL;attr.stack_size=1024;attr.priority=osPriorityNormal;// 将线程启动osThreadId_ttid=osThreadNew((osThreadFunc_t)printThread,NULL,&attr);if(tid==NULL){printf("[Thread Test] Failed to create printThread!\r\n");}// 休眠5秒osDelay(500);// 终止线程osStatus_tstatus=osThreadTerminate(tid);printf("[Thread Test] printThread stop, status = %d.\r\n",status);}APP_FEATURE_INIT(threadTest);
  1. 编写gn文件。

static_library("thread_demo"){
    sources = [
        "singleThread.c"
    ]
    include_dirs = [
        "//commonlibrary/utils_lite/include",
        "//device/soc/hisilicon/hi3861v100/hi3861_adapter/kal/cmsis"
    ]
}
  1. 编写app下的gn文件。

OpenHarmony智能开发套件[内核编程·上]-开源基础软件社区

注意的是,这次的写法与上次不同,是因为笔者的样例文件名和静态模块的名字是一样的就可以简写。

执行效果

OpenHarmony智能开发套件[内核编程·上]-开源基础软件社区

多线程的封装

在处理业务的时候,我们一般是多线程的背景,下面笔者将创建线程函数封装起来,方便大家创建多线程。

osThreadId_tnewThread(char*name,osThreadFunc_tfunc,void*arg){// 定义线程和属性osThreadAttr_tattr={name,0,NULL,0,NULL,1024,osPriorityNormal,0,0};// 创建线程osThreadId_ttid=osThreadNew(func,arg,&attr);if(tid==NULL){printf("[newThread] osThreadNew(%s) failed.\r\n",name);}returntid;}

线程部分先体会到这里,想要探索更过线程相关的API,笔者这里提供了API网站,供大家参考学习。

CMSIS_OS2 Thread API

软件定时器

下面我们介绍软件定时器,老样子我们先来介绍以下软件定时器。软件定时器是一种在软件层面上实现的计时器机制,用于在特定的时间间隔内执行特定的任务或触发特定的事件。它不依赖于硬件定时器,而是通过软件编程的方式实现。举一个例子,手机应用。

当你使用手机上的某个应用时,你可能会注意到,如果你在一段时间内没有进行任何操作,应用程序会自动断开连接并要求你重新登录。这是为了保护你的账号安全并释放服务器资源。类似的设定都是有软件定时器实现的,下面进行实际操作,让大家体会一下软件定时器。

任务

创建一个软件定时器,用来模拟上述手机应用的例子。为了方便理解,假设从此刻开始,我们不对手机做任何操作,也就是说,我们的回调函数只需要单纯的计算应用不被操作的时常即可。

操作
  1. 新建样例目录
    applications/sample/wifi-iot/app/thread_demo。

  2. 新建源文件和gn文件
    applications/sample/wifi-iot/app/thread_demo/singleThread.c。
    applications/sample/wifi-iot/app/thread_demo/BUILD.gn。

  3. 编写源码

创建软件定时器。

osTimerId_tosTimerNew(osTimerFunc_tfunc,osTimerType_ttype,void*argument,constosTimerAttr_t*attr);
  • func: 软件定时器的回调函数。

  • type:软件定时器的种类。

  • argument:软件定时器回调函数的参数。

  • attr:软件定时器的属性。

返回值:返回软件定时器的id, id为空则说明软件定时器失败。

typedefenum{/** One-shot timer */osTimerOnce=0,/** Repeating timer */osTimerPeriodic=1}osTimerType_t;

软件定时器的种类有两个,分为一次性定时器和周期性定时器,一次性在执行完回调函数后就会停止计数,而周期性定时器会重复触发,每次触发重新计时。根据不同的需求我们可以选择使用不同的软件定时器。

启动软件定时器。

osStatus_tosTimerStart(osTimerId_ttimer_id,uint32_tticks);
  • timer_id:软件定时器的参数,指定要启动哪个软件定时器。

  • ticks:等待多少个ticks执行回调函数,在Hi3861中 100个ticks为1秒。

  • 返回值:软件定时器的状态码,在线程部分已经展示给大家了全部的状态码。

停止定时器。

osStatus_tosTimerStop(osTimerId_ttimer_id);

这个函数很简单,只需要传软件定时器的id,即可停止软件计时器,并且返回他的状态码。

删除定时器。

osStatus_tosTimerDelete(osTimerId_ttimer_id);

删除和停止类似,就不多说明了。

下面是源代码。

#include<stdio.h>#include"ohos_init.h"// CMSIS#include"cmsis_os2.h"// POSIX#include<unistd.h>// 为操作软件的时间staticinttimes=0;// 软件定时器回调函数voidtimerFunction(void){times++;printf("[Timer Test] Timer is Running, times = %d.\r\n",times);}// 主函数voidtimerMain(void){// 创建软件定时器osTimerId_ttid=osTimerNew(timerFunction,osTimerPeriodic,NULL,NULL);if(tid==NULL){printf("[Timer Test] Failed to create a timer!\r\n");return;}else{printf("[Timer Test] Create a timer success!\r\n");}// 启动软件定时器,每1秒执行一次回调函数osStatus_tstatus=osTimerStart(tid,100);// 当超过三个周期位操作软件时,关闭软件while(times<=3){osDelay(100);}// 停止软件定时器status=osTimerStop(tid);// 删除软件定时器status=osTimerDelete(tid);printf("[Timer Test] Time Out!\r\n");}voidTimerTest(void){// 创建测试线程osThreadAttr_tattr;attr.name="timerMain";attr.attr_bits=0U;attr.cb_mem=NULL;attr.cb_size=0U;attr.stack_mem=NULL;attr.stack_size=0U;attr.priority=osPriorityNormal;// 启动测试线程osThreadId_ttid=osThreadNew((osThreadFunc_t)timerMain,NULL,&attr);if(tid==NULL){printf("[Timer Test] Failed to created timerMain!\r\n");}}APP_FEATURE_INIT(TimerTest);
  1. 编写gn文件。

static_library("timer_demo"){
    sources = [
        "timer.c"
    ]
    include_dirs = [
        "//commonlibrary/utils_lite/include",
        "//device/soc/hisilicon/hi3861v100/hi3861_adapter/kal/cmsis"
    ]
}
  1. 编写app下的gn文件。

OpenHarmony智能开发套件[内核编程·上]-开源基础软件社区

执行效果

OpenHarmony智能开发套件[内核编程·上]-开源基础软件社区

软件定时器的API相对较少,这里还是提供所有的软件定时器API。

CMSIS_OS2 Timer API

结束语

本篇主要介绍了一些基础内核编程相关的内容,希望能够帮助到学习OpenHarmony的伙伴们,考虑到篇幅问题,剩余的基础内核编程将在OpenHarmony智能开发套件[内核编程·上]中介绍。

想了解更多关于开源的内容,请访问:

51CTO 开源基础软件社区

https://ost.51cto.com

20    2023-05-15 15:27:20    鸿蒙 智能开发套件