赞
踩
在继续后面课程的内容讲解前,我们要知道在H3861平台上编写的代码到底是如何启动的,这一点很重要。
先分析HelloWorld程序的启动运行流程,并顺便讲解OpenHarmony在H3861平台的,系统是从哪里启动的。
反着推导函数之间具体的调用链
我们先编写一个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.c static 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_CALL INIT_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_LITE printf("[Foundation][D] Start server system, begin. \n"); struct timeval tvBefore; (void)gettimeofday(&tvBefore, NULL); #endif OHOS_SystemInit(); #ifdef DEBUG_SERVICES_SAFWK_LITE struct 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); #endif while (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
程序启动和运行的完整流程。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。