当前位置:   article > 正文

基于Hi3861平台的OpenHarmony程序是如何启动运行的_openharmony系统启动入口

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);

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

将业务构建成静态库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"
   ]
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

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

import("//build/lite/config/component/lite_component.gni")
lite_component("app") {
    features = [
         "my_app:myapp",
    ]
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

使用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")

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

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

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

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

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

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

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

那么我们来全局搜索下:.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)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

我们从上面的代码可以看出来这几个代码是有联系的,看不懂不要紧,我们接着往下看(稍后再来看上面的代码),打开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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

我们看到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)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

我们再精简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"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

最后通过(*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")
  • 1
  • 2

那么它最终表达如下:

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

同样的,你就当做我们是第一次看这个源码,啥也不懂,我们只会全局搜索一下:.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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

我们发现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();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

这个时候,我们肯定是不知道谁在用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();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/223438
推荐阅读
相关标签
  

闽ICP备14008679号