当前位置: 首页 > 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;检验程序中的每条通路是否都能按预定要求正确工作。白盒测试的测试方法有代码检查法、静态结…...

java_网络服务相关_gateway_nacos_feign区别联系

1. spring-cloud-starter-gateway 作用&#xff1a;作为微服务架构的网关&#xff0c;统一入口&#xff0c;处理所有外部请求。 核心能力&#xff1a; 路由转发&#xff08;基于路径、服务名等&#xff09;过滤器&#xff08;鉴权、限流、日志、Header 处理&#xff09;支持负…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

视频字幕质量评估的大规模细粒度基准

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用&#xff0c;因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型&#xff08;VLMs&#xff09;在字幕生成方面…...

高危文件识别的常用算法:原理、应用与企业场景

高危文件识别的常用算法&#xff1a;原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件&#xff0c;如包含恶意代码、敏感数据或欺诈内容的文档&#xff0c;在企业协同办公环境中&#xff08;如Teams、Google Workspace&#xff09;尤为重要。结合大模型技术&…...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

今日科技热点速览

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

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

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

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)

前言&#xff1a; 最近在做行为检测相关的模型&#xff0c;用的是时空图卷积网络&#xff08;STGCN&#xff09;&#xff0c;但原有kinetic-400数据集数据质量较低&#xff0c;需要进行细粒度的标注&#xff0c;同时粗略搜了下已有开源工具基本都集中于图像分割这块&#xff0c…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

基于Java+VUE+MariaDB实现(Web)仿小米商城

仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意&#xff1a;运行前…...