1 背景

学习一个系统,先从启动过程开始。鸿蒙的wifiiot的初始化过程,涉及的文件并不多,但因为用到了一些复杂一些的宏定义,理解起来比较困难。

2 开发板系统的入口app_main

文件:vendor/hisi/hi3861/hi3861/app/wifiiot_app/src/app_main.c
鸿蒙wifiiot为单进程系统,整个系统的入口程序为app_main,但这个并没有找到明确的文档说明。
这件文件一开始部分,是定义了开发板相关的一些硬件设备的初始化过程,而最后一行,才真正进入鸿蒙操作系统的初始化。

void __attribute__((weak)) HOS_SystemInit(void)
{
    return;
}

hi_void app_main(hi_void)
{
    const hi_char* sdk_ver = hi_get_sdk_version();
    printf("sdk ver:%s\r\n", sdk_ver);

    hi_flash_partition_table *ptable = HI_NULL;

    peripheral_init();
    peripheral_init_no_sleep();

    hi_u32 ret = hi_factory_nv_init(HI_FNV_DEFAULT_ADDR, HI_NV_DEFAULT_TOTAL_SIZE, HI_NV_DEFAULT_BLOCK_SIZE);

    /* partion table should init after factory nv init. */
    ret = hi_flash_partition_init();
    ptable = hi_get_partition_table();

    ret = hi_nv_init(ptable->table[HI_FLASH_PARTITON_NORMAL_NV].addr, ptable->table[HI_FLASH_PARTITON_NORMAL_NV].size,
        HI_NV_DEFAULT_BLOCK_SIZE);
    /* if not use file system, there is no need init it */
    hi_fs_init();

    (hi_void)hi_event_init(APP_INIT_EVENT_NUM, HI_NULL);
    hi_sal_init();
    /* 此处设为TRUE后中断中看门狗复位会显示复位时PC值,但有复位不完全风险,量产版本请务必设为FALSE */
    hi_syserr_watchdog_debug(HI_FALSE);
    /* 默认记录宕机信息到FLASH,根据应用场景,可不记录,避免频繁异常宕机情况损耗FLASH寿命 */
    hi_syserr_record_crash_info(HI_TRUE);

    hi_lpc_init();
    hi_lpc_register_hw_handler(config_before_sleep, config_after_sleep);

    /* 如果不需要使用Histudio查看WIFI驱动运行日志等,无需初始化diag */
    /* if not use histudio for diagnostic, diag initialization is unnecessary */
    /* Shell and Diag use the same uart port, only one of them can be selected */
#ifndef CONFIG_FACTORY_TEST_MODE
    (hi_void)hi_shell_init();
    tcpip_init(NULL, NULL);
    ret = hi_wifi_init(APP_INIT_VAP_NUM, APP_INIT_USR_NUM);
    HOS_SystemInit();
}

这里有一个小技巧,HOS_SystemInit本地已有一个空定义了,它怎么会调用到操作系统的初始化函数里呢。
这个主要是编译宏__attribute__((weak)) 在起作用,这告诉编译器,这是一个弱引用,如果有其他的HOS_SystemInit的定义,优先用其他部分的。

3 鸿蒙初始化

文件:base/startup/services/bootstrap_lite/source/system_init.c
这里宏的引用特别复杂,宏的定义在:base/startup/services/bootstrap_lite/source/core_main.h

void HOS_SystemInit(void)
{
    MODULE_INIT(bsp);
    MODULE_INIT(device);
    MODULE_INIT(core);
    SYS_INIT(service);
    SYS_INIT(feature);
    MODULE_INIT(run);
    SAMGR_Bootstrap();
}

对于MODULE_INIT(run),实际展开后的代码如下:
对于0-4的优先级不同的run段,分别取段首和段尾,然后依次执行段中定义的函数指针。
但这里有一个问题,这些函数指针在哪里进行初始化呢?这就汲及到各个模块的初始化定义代码。

    do {
        do {
            InitCall *initcall = (InitCall *) (__section_begin(".zinitcall." "run" "0" ".init"));
            InitCall *initend = (InitCall *) (__section_end(".zinitcall." "run" "0" ".init"));
            for (; initcall < initend; initcall++) { (*initcall)(); }
        }
        while (0);
        do {
            InitCall *initcall = (InitCall *) (__section_begin(".zinitcall." "run" "1" ".init"));
            InitCall *initend = (InitCall *) (__section_end(".zinitcall." "run" "1" ".init"));
            for (; initcall < initend; initcall++) { (*initcall)(); }
        }
        while (0);
        do {
            InitCall *initcall = (InitCall *) (__section_begin(".zinitcall." "run" "2" ".init"));
            InitCall *initend = (InitCall *) (__section_end(".zinitcall." "run" "2" ".init"));
            for (; initcall < initend; initcall++) { (*initcall)(); }
        }
        while (0);
        do {
            InitCall *initcall = (InitCall *) (__section_begin(".zinitcall." "run" "3" ".init"));
            InitCall *initend = (InitCall *) (__section_end(".zinitcall." "run" "3" ".init"));
            for (; initcall < initend; initcall++) { (*initcall)(); }
        }
        while (0);
        do {
            InitCall *initcall = (InitCall *) (__section_begin(".zinitcall." "run" "4" ".init"));
            InitCall *initend = (InitCall *) (__section_end(".zinitcall." "run" "4" ".init"));
            for (; initcall < initend; initcall++) { (*initcall)(); }
        }
        while (0);
    }
    while (0);

4 模块的初始化

如下文件中,只是定了一个SYS_RUN,但并没有修改上面二个章节提到的启动函数,为什么它就可以在初始化的时候并调用到了呢?

applications/sample/wifi-iot/app/demolink/helloworld.c:24:SYS_RUN(DemoSdkMain);
applications/sample/wifi-iot/app/iothardware/led_example.c:86:SYS_RUN(LedExampleEntry);
applications/sample/wifi-iot/app/samgr/bootstrap_example.c:206:SYS_RUN(MRun);

代码如下:

#include "ohos_init.h"
#include "demosdk.h"

void DemoSdkMain(void)
{
    DemoSdkEntry();
}

SYS_RUN(DemoSdkMain);

这里的一切,还是编译宏在启作用,宏定义文件: utils/native/lite/include/hos_init.h
具体的宏我们就不看了,还是直接看展开后的代码,可以看到,它定义了一个函数指针__zinitcall_run_DemoSdkMain,实际指向函数DemoSdkMain,它存储在(".zinitcall." “run” “2” “.init”)的section中,正是上面鸿蒙初始化时用到的优先有为2的section. 当HOS_SystemInit在调用时,则会获取到此sections里的函数指针,并进行调用。

void DemoSdkMain(void)
{
    std::cout << "DemoSdkMain" << std::endl;
};

static const InitCall __attribute__((used)) __zinitcall_run_DemoSdkMain __attribute__((section(".zinitcall." "run" "2" ".init"))) = DemoSdkMain;

5 参考资料

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐