当前位置: 首页 > news >正文

基于Hi3861平台的OpenHarmony程序是如何启动运行的

一、前言

在继续后面课程的内容讲解前,我们要知道在H3861平台上编写的代码到底是如何启动的,这一点很重要。
先分析HelloWorld程序的启动运行流程,并顺便讲解OpenHarmony在H3861平台的,系统是从哪里启动的。
反着推导函数之间具体的调用链

二、编写Hello World代码

我们先编写一个HelloWorld的程序,然后看它是怎么构建编译和运行的

// HelloWorld.c#include <stdio.h>
#include "ohos_init.h"void Hello_World(void)
{printf("Hello World!\r\n");
}
APP_FEATURE_INIT(Hello_World);

将业务构建成静态库的BUILD.gn文件,内容如下:

// static_library里面:指定了业务模块的编译结果,为静态库文件:libmyapp.a
static_library("myapp") {// sources里面:指定了静态库.a所依赖的.c文件及其路径sources = ["hello_world.c"]// include_dirs里面:指定了source所需要依赖的.h文件路径include_dirs = ["//utils/native/lite/include"]
}

编写模块BUILD.gn文件,在./applications/BearPi/BearPi-HM/sample 下的BUILD.gn文件中添加如下代码:

import("//build/lite/config/component/lite_component.gni")
lite_component("app") {features = ["my_app:myapp",]
}

使用vscode 编译,烧录成功,启动开发板,运行,那么它在启动运行的过程中,代码是如何执行的?

三、程序是如何运行被调用的

我们从上面看到 HelloWorld.c 里面使用了 APP_FEATURE_INIT ,那么我们是不是可以先从这里入手,查看它里面到底做了什么,再展开看到底是如何执行的,我们接着往下看:

APP_FEATURE_INIT 它定义在了 ohos_init.h 里面:

// 代码在:utils/native/lite/include/ohos_init.h// 函数指针
typedef void (*InitCall)(void);...#define LAYER_INITCALL(func, layer, clayer, priority)            \static const InitCall USED_ATTR __zinitcall_##layer##_##func \__attribute__((section(".zinitcall." clayer #priority ".init"))) = func
#endif
// Default priority is 2, priority range is [0, 4]
#define LAYER_INITCALL_DEF(func, layer, clayer) \LAYER_INITCALL(func, layer, clayer, 2)...#define APP_FEATURE_INIT(func) LAYER_INITCALL_DEF(func, app_feature, "app.feature")

那么我们可以知道LAYER_INITCALL 传递的数据等价于下面这样:

LAYER_INITCALL(func, app_feature, "app.feature", 2)

那么此时的clayer就是:app.feature#priority 等于 2,展开(func, layer, clayer, priority) 得到如下内容:

 static const InitCall USED_ATTR __zinitcall_##layer##_##func \__attribute__((section(".zinitcall.app.feature2.init"))) = func

到这里,如何继续往下看呢?

假设大家和我一样都是第一次看,完全不知道具体目录和文件是干啥用的,就是硬撕代码。

那么我们来全局搜索下:.zinitcall.app. 这个字符串,发现它在 bootstrap_service.h 这个头文件里面有使用,我们看一下:

// 代码在: ohos_bundles/@ohos/bootstrap/source/bootstrap_service.h#define APP_NAME(name, step) ".zinitcall.app." #name #step ".init"...#define APP_CALL(name, step)                                      \do {                                                          \InitCall *initcall = (InitCall *)(APP_BEGIN(name, step)); \InitCall *initend = (InitCall *)(APP_END(name, step));    \for (; initcall < initend; initcall++) {                  \(*initcall)();                                        \}                                                         \} while (0)...#define APP_BEGIN(name, step)                                 \({  extern InitCall __zinitcall_app_##name##_start;       \InitCall *initCall = &__zinitcall_app_##name##_start; \(initCall);                                           \})#define APP_END(name, step)                                 \({  extern InitCall __zinitcall_app_##name##_end;       \InitCall *initCall = &__zinitcall_app_##name##_end; \(initCall);                                         \})...#define APP_BEGIN(name, step) __section_begin(APP_NAME(name, step))...#define INIT_APP_CALL(name) \do {                    \APP_CALL(name, 0);  \APP_CALL(name, 1);  \APP_CALL(name, 2);  \APP_CALL(name, 3);  \APP_CALL(name, 4);  \} while (0)

我们从上面的代码可以看出来这几个代码是有联系的,看不懂不要紧,我们接着往下看(稍后再来看上面的代码),打开bootstrap_service.c 看里面的代码:

// 代码在:base/startup/services/bootstrap_lite/source/bootstrap_service.cstatic void Init(void)
{static Bootstrap bootstrap;bootstrap.GetName = GetName;bootstrap.Initialize = Initialize;bootstrap.MessageHandle = MessageHandle;bootstrap.GetTaskConfig = GetTaskConfig;bootstrap.flag = FALSE;// 注册Bootstrap服务SAMGR_GetInstance()->RegisterService((Service *)&bootstrap);
}
SYS_SERVICE_INIT(Init);...static BOOL MessageHandle(Service *service, Request *request)
{Bootstrap *bootstrap = (Bootstrap *)service;switch (request->msgId) {case BOOT_SYS_COMPLETED:  // 核心系统服务已初始化完成的消息if ((bootstrap->flag & LOAD_FLAG) != LOAD_FLAG) {// 这里调用了INIT_APP_CALLINIT_APP_CALL(service);INIT_APP_CALL(feature);bootstrap->flag |= LOAD_FLAG;}(void)SAMGR_SendResponseByIdentity(&bootstrap->identity, request, NULL);break;case BOOT_APP_COMPLETED:  // 系统和应用层服务初始化完成的消息(void)SAMGR_SendResponseByIdentity(&bootstrap->identity, request, NULL);break;case BOOT_REG_SERVICE:    // 运行中服务注册的消息(void)SAMGR_SendResponseByIdentity(&bootstrap->identity, request, NULL);break;default:break;}return TRUE;
}

我们看到MessageHandle里面,当消息ID为BOOT_SYS_COMPLETED时,调用了INIT_APP_CALL(feature)方法,我们再回到上面bootstrap_service.h里面的代码:

// 代码在: ohos_bundles/@ohos/bootstrap/source/bootstrap_service.h#define APP_NAME(name, step) ".zinitcall.app." #name #step ".init"...#define APP_CALL(name, step)                                      \do {                                                          \InitCall *initcall = (InitCall *)(APP_BEGIN(name, step)); \InitCall *initend = (InitCall *)(APP_END(name, step));    \for (; initcall < initend; initcall++) {                  \(*initcall)();                                        \}                                                         \} while (0)...#define APP_BEGIN(name, step)                                 \({  extern InitCall __zinitcall_app_##name##_start;       \InitCall *initCall = &__zinitcall_app_##name##_start; \(initCall);                                           \})...#define APP_BEGIN(name, step) __section_begin(APP_NAME(name, step))...#define INIT_APP_CALL(name) \do {                    \APP_CALL(name, 0);  \APP_CALL(name, 1);  \APP_CALL(name, 2);  \APP_CALL(name, 3);  \APP_CALL(name, 4);  \} while (0)

我们再精简APP_CALL(feature,2) 里面代码的调用:

InitCall *initcall = (InitCall *)(APP_BEGIN(name, step));#define APP_BEGIN(name, step) __section_begin(APP_NAME(name, step))#define APP_NAME(name, step) ".zinitcall.app." #name #step ".init"<==========↓↓↓↓最终它的值等于下面↓↓↓↓==========>#define APP_NAME(name, step) ".zinitcall.app. feature 2 .init"

最后通过(*initcall)() 调用方法,至此,我们就初步知道 APP_FEATURE_INIT是如何被调用到的了。

结束了吗?没有,我们往下看,系统到底是从哪里启动的?这样才能完整的串起来。

四、系统启动入口

我们在 bootstrap_service.c 里面看到了这段代码: SYS_SERVICE_INIT(Init),它是干什么用的?

// 代码在:utils/native/lite/include/ohos_init.h
#define SYS_SERVICE_INIT(func) LAYER_INITCALL_DEF(func, sys_service, "sys.service")

那么它最终表达如下:

 static const InitCall USED_ATTR __zinitcall_##layer##_##func \__attribute__((section(".zinitcall.sys.service2.init"))) = func

同样的,你就当做我们是第一次看这个源码,啥也不懂,我们只会全局搜索一下:.zinitcall.sys.,看看哪里使用了它,发现 core_main.h 有使用

//代码在:ohos_bundles/@ohos/bootstrap/source/core_main.h#define SYS_CALL(name, step)                                      \do {                                                          \InitCall *initcall = (InitCall *)(SYS_BEGIN(name, step)); \InitCall *initend = (InitCall *)(SYS_END(name, step));    \for (; initcall < initend; initcall++) {                  \(*initcall)();                                        \}                                                         \} while (0)...#define SYS_BEGIN(name, step)                                 \({        extern InitCall __zinitcall_sys_##name##_start;       \InitCall *initCall = &__zinitcall_sys_##name##_start; \(initCall);                                           \})
...#define SYS_BEGIN(name, step) __section_begin(SYS_NAME(name, step))...#define SYS_INIT(name)     \do {                   \SYS_CALL(name, 0); \SYS_CALL(name, 1); \SYS_CALL(name, 2); \SYS_CALL(name, 3); \SYS_CALL(name, 4); \} while (0)

我们发现SYS_INIT是在 system_init.c 里面被调用的

// 代码在:ohos_bundles/@ohos/bootstrap/source/system_init.c
void OHOS_SystemInit(void)
{MODULE_INIT(bsp);MODULE_INIT(device);MODULE_INIT(core);SYS_INIT(service);SYS_INIT(feature);MODULE_INIT(run);// 启动系统服务和功能SAMGR_Bootstrap();
}

这个时候,我们肯定是不知道谁在用void OHOS_SystemInit(void),那么动动手指,搜索一下,你会发现,OpenHarmony系统真正的启动入口main函数

// 代码在:foundation/distributedschedule/services/safwk_lite/src/main.c...int main(int argc, char * const argv[])
{
#ifdef DEBUG_SERVICES_SAFWK_LITEprintf("[Foundation][D] Start server system, begin. \n");struct timeval tvBefore;(void)gettimeofday(&tvBefore, NULL);
#endifOHOS_SystemInit();#ifdef DEBUG_SERVICES_SAFWK_LITEstruct timeval tvAfter;(void)gettimeofday(&tvAfter, NULL);printf("[Foundation][D] Start server system, end. duration %d seconds and %d microseconds. \n",\tvAfter.tv_sec - tvBefore.tv_sec, tvAfter.tv_usec - tvBefore.tv_usec);
#endifwhile (1) {// pause only returns when a signal was caught and the signal-catching function returned.// pause only returns -1, no need to process the return value.(void)pause();}
}

我们能看到OHOS_SystemInit它是在main.c中调用的,至此,我们豁然开朗。

纵观上面的代码分析,我们学习了系统从哪里启动的,并且知道了 HelloWorld 程序启动和运行的完整流程。

相关文章:

基于Hi3861平台的OpenHarmony程序是如何启动运行的

一、前言 在继续后面课程的内容讲解前&#xff0c;我们要知道在H3861平台上编写的代码到底是如何启动的&#xff0c;这一点很重要。 先分析HelloWorld程序的启动运行流程&#xff0c;并顺便讲解OpenHarmony在H3861平台的&#xff0c;系统是从哪里启动的。 反着推导函数之间具体…...

2023彻底解决Typora使用iPic微博图床失效问题

一、问题描述用Typora搭配iPic图床使用&#xff0c;最近csdn图片显示不出来用浏览器打开图片显示403&#xff0c;这里原因是微博图床出问题了导致的而使用iPic其他图床则需要一直付费&#xff0c;那有没有一劳永逸的解决所有问题呢&#xff1f;二、旧图恢复首先怎么找回旧图&am…...

Revit中添加水平仰视平面图及水平剖面

一、 Revit中如何添加水平仰视平面图 在Revit平面视图中视角是俯视视角&#xff0c;但是在一些特殊的情况下&#xff0c;我们可能需要创建仰视视角的平面视图&#xff0c;例如我们需要向上看天花板的灯具布置的时候。 1.下面讲一下如何在添加仰视平面视图的方法。如图1在模型中…...

Python 循环语句

Python的循环语句&#xff0c;程序在一般情况下是按顺序执行的。编程语言提供了各种控制结构&#xff0c;允许更复杂的执行路径。循环语句允许我们执行一个语句或语句组多次&#xff0c;下面是在大多数编程语言中的循环语句的一般形式&#xff1a;Python 提供了 for 循环和 whi…...

使用 ThreeJS 实现第一个三维场景(详)

文章目录参考描述index.html三维场景的基本实现导入 ThreeJS准备工作场景摄像机视锥体正交摄像机透视摄像机渲染器后续处理将摄像机添加至场景中移动摄像机设置画布尺寸将渲染器创建的画布添加到 HTML 元素中渲染物体结构材质合成将物体添加至场景中代码总汇执行效果动画reques…...

《小猫猫大课堂》三轮5——动态内存管理(通讯录动态内存化)

宝子&#xff0c;你不点个赞吗&#xff1f;不评个论吗&#xff1f;不收个藏吗&#xff1f; 最后的最后&#xff0c;关注我&#xff0c;关注我&#xff0c;关注我&#xff0c;你会看到更多有趣的博客哦&#xff01;&#xff01;&#xff01; 喵喵喵&#xff0c;你对我真的很重…...

【Selenium学习】Selenium 八大定位法

1.1 ID定位HTML Tag 的 id 属性值是唯一的&#xff0c;故不存在根据 id 定位多个元素的情况。下面以在百度首页搜索框输入文本“python”为例。搜索框的 id 属性值为“kw”&#xff0c;如图1.1所示&#xff1a;代码如下&#xff0c;“find_element_by_id”方法已废弃&#xff0…...

算法训练营 day41 贪心算法 单调递增的数字 买卖股票的最佳时机含手续费

算法训练营 day41 单调递增的数字 买卖股票的最佳时机含手续费 单调递增的数字 738. 单调递增的数字 - 力扣&#xff08;LeetCode&#xff09; 当且仅当每个相邻位数上的数字 x 和 y 满足 x < y 时&#xff0c;我们称这个整数是单调递增的。 给定一个整数 n &#xff0c…...

【数据结构-JAVA】排序

排序在现实生活中的应用可谓相当广泛&#xff0c;比如电商平台中&#xff0c;选购商品时&#xff0c;使用价格排序或是综合排序、高考填报志愿的时候&#xff0c;会参考全国大学排名的情况。下面介绍一些计算机中与排序相关的概念&#xff1a;排序&#xff1a;所谓排序&#xf…...

基于注解管理Bean

一、介绍从 Java 5 开始&#xff0c;Java 增加了对注解&#xff08;Annotation&#xff09;的支持&#xff0c;它是代码中的一种特殊标记&#xff0c;可以在编译、类加载和运行时被读取&#xff0c;执行相应的处理。开发人员可以通过注解在不改变原有代码和逻辑的情况下&#x…...

Containerd 的 Bug 导致容器被重建!如何避免?

作者简介邓宇星&#xff0c;SUSE Rancher 中国区软件架构师&#xff0c;6 年云原生领域经验&#xff0c;参与Rancher 1.x 到 Rancher 2.x 版本迭代&#xff0c;目前负责 Rancher For openEuler(RFO) 项目开发。最近我们关注到一个关于 containerd 运行时的 issue(​​https://g…...

win环境安装部署Jenkins

win环境安装部署Jenkins&#xff0c;2022年11月&#xff1a;从2022年 6 月 28 日发布的 Jenkins 2.357 和2022年9 月发布的 LTS 版本开始&#xff0c;Jenkins 需要 Java 11 才能使用&#xff0c;放弃 Java 8&#xff0c;如果用JDK1.8&#xff0c;那么Jenkins版本需要是2.357版本…...

网络变压器与不同芯片之间的匹配原则及POE通讯产品需要注意哪些方面

Hqst盈盛电子导读&#xff1a;网络变压器与不同芯片之间的匹配原则及POE通讯产品需要注意哪些方面网络变压器与不同芯片之间的匹配原则&#xff1a;一&#xff0c;电流型PHY芯片一般要配的网络变压器&#xff1a;1、变压器PHY侧3线共模电感 &#xff08;更适合POE产品&#xff…...

Spring WebFlux

目录 基于注解编程模型 函数式编程模型 传统的基于Servlet的Web框架,如Spring MVC,在本质上都是阻塞和多线程的,每个连接都会使用一个线程。在请求处理的时候,会在线程池中拉取一个工作者( worker )线程来对请求进行处理。同时,请求线程是阻塞的,直到工作者线程提示它已…...

C++基础面试题:new和malloc的区别

面试题&#xff1a;new和malloc的区别或new和malloc的异同 相同点&#xff1a; 1、new/delete和malloc/free它们都是内存申请和释放的函数。 2、new/delete和malloc/free 都要一一对应&#xff0c;调用了多少次new 就需要调用多少次delete&#xff1b;同 理调用多少次ma…...

WebDAV之葫芦儿·派盘+KMPlayer

KMPlayer 支持WebDAV方式连接葫芦儿派盘。 KMPlayer几乎可以播放您系统上所有的影音文件,支持几乎全部音视频格式。通过其强大的插件功能,可以支持层出不穷的新格式。软件还具有齐全的操控功能,支持捕获音频、捕获AVI、捕获画面、外挂字幕、自定义编辑设置,是视频爱好者的不…...

杨浦区人工智能及大数据(云计算)企业登记工作(2023年度)的通知

各相关单位&#xff1a; 根据《“长阳秀带”在线新经济产业集聚发展若干政策》&#xff08;杨府发〔2022〕2号&#xff09;及其实施细则的要求&#xff0c;现组织开展2023年度杨浦区人工智能与大数据(云计算&#xff09;企业登记备案工作,现将相关工作通知如下&#xff1a; 一…...

2023年去培训机构学前端还是Java?

选择专业肯定是优先考虑更有发展前景和钱途的专业。毕竟IT专业的培训费都不低&#xff0c;基本都要一两万左右&#xff0c;咱们花钱总是希望获得最大回报。 那么到底哪个更有发展前景呢&#xff1f; 零基础能学得会吗&#xff1f; 就业薪资如何呢&#xff1f; 前言 不知道大家有…...

【React】组件事件

React(二) 创建组件 函数组件 函数组件&#xff1a;使用JS的函数或者箭头函数创建的组件 使用 JS 的函数(或箭头函数)创建的组件&#xff0c;叫做函数组件约定1&#xff1a;函数名称必须以大写字母开头&#xff0c;React 据此区分组件和普通的 HTML约定2&#xff1a;函数组…...

黑/白盒测试说明

白盒测试白盒测试也称结构测试或逻辑驱动测试&#xff0c;它是按照程序内部的结构测试程序&#xff0c;通过测试来检测产品内部动作是否按照设计规格说明书的规定正常进行&#xff0c;检验程序中的每条通路是否都能按预定要求正确工作。白盒测试的测试方法有代码检查法、静态结…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…...

循环冗余码校验CRC码 算法步骤+详细实例计算

通信过程&#xff1a;&#xff08;白话解释&#xff09; 我们将原始待发送的消息称为 M M M&#xff0c;依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)&#xff08;意思就是 G &#xff08; x ) G&#xff08;x) G&#xff08;x) 是已知的&#xff09;&#xff0…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

今日科技热点速览

&#x1f525; 今日科技热点速览 &#x1f3ae; 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售&#xff0c;主打更强图形性能与沉浸式体验&#xff0c;支持多模态交互&#xff0c;受到全球玩家热捧 。 &#x1f916; 人工智能持续突破 DeepSeek-R1&…...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南

&#x1f680; C extern 关键字深度解析&#xff1a;跨文件编程的终极指南 &#x1f4c5; 更新时间&#xff1a;2025年6月5日 &#x1f3f7;️ 标签&#xff1a;C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言&#x1f525;一、extern 是什么&#xff1f;&…...

聊一聊接口测试的意义有哪些?

目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开&#xff0c;首…...

MySQL 知识小结(一)

一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库&#xff0c;分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷&#xff0c;但是文件存放起来数据比较冗余&#xff0c;用二进制能够更好管理咱们M…...

纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join

纯 Java 项目&#xff08;非 SpringBoot&#xff09;集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...

多模态图像修复系统:基于深度学习的图片修复实现

多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...