[HarmonyOS之旅] Chapter5 – 有趣的init

大家晚上好。不知道大家还记不记得上一篇的最后我们留下了一个问题呢?

前情回顾:

参考HarmonyOS的文档,我们写了第一个应用程序。

void Init_Trace(void) { printf(“[codinglab] >>>> Hello HarmonyOS\n”); }; SYS_RUN(Init_Trace);

并添加了两句打印

void HOS_SystemInit(void) { printf(“[codinglab] >>>> start\n”); MODULE_INIT(bsp); MODULE_INIT(device); MODULE_INIT(core); SYS_INIT(service); SYS_INIT(feature); MODULE_INIT(run); printf(“[codinglab] >>>> end\n”); SAMGR_Bootstrap(); }

得到的结果是这样的。

[HarmonyOS之旅]  Chapter5 - 有趣的init

why?

这里遗留的问题就是我们在新增了测试函数之后,并没有在任何地方显示的调用它,但是实际运行的时候,它却被运行到了,这是为什么呢?答案就在本篇来揭晓,下面就和小A一起来看看这个有趣的机制吧。

首先我们在函数定义之后唯一做的就是做了个SYS_RUN(Init_Trace);它是调用吗?我们来看一看。

#define SYS_RUN(func) LAYER_INITCALL_DEF(func, run, “run”) #define LAYER_INITCALL_DEF(func, layer, clayer) \ LAYER_INITCALL(func, layer, clayer, 2) #define LAYER_INITCALL(func, layer, clayer, priority) \ static const InitCall USED_ATTR __zinitcall_##layer##_##func \ __attribute__((section(“.zinitcall.” clayer #priority “.init”))) = func #define USED_ATTR __attribute__((used)) typedef void (*InitCall)(void);

what?

乍看起来是不是有点懵,就像俄罗斯套娃一样,一宏接一宏。这里的阅读顺序应该是自下而上的阅读。我们一个个来看

typedef void (*InitCall)(void);

无需多说。

USED_ATTR。

我们知道的是__attribute__是用来指定编译属性的, 这是是通知编译器在目标文件中保留一个静态函数,即使它没有被引用, 这样编译过程中,既不会因为被定义了但没有使用而报错,也不会在链接的时候被删除掉。

LAYER_INITCALL。

__attribute__((section(“name”))) = func, 作用是将作用对象func指定放入到名为name的段中。(这里关于段的概念不在展开)。 注意这个是要搭配链接脚本用的。

SYS_RUN

这里简化一下就是:Initcall _zinitcall_run_func = func, 其实就是定义了一个函数指针(_zinitcall_run_func)并把它指向了func, 然后根据attribute的修饰,在编译的时候把它放到了.zinitcall.run2.init段当中。

说到现在好像还是没能看到怎么调用的。不要急,我们来看一下MODULE_INIT(run);

#define MODULE_INIT(name) \ do { \ MODULE_CALL(name, 0); \ } while (0) #define MODULE_CALL(name, step) \ do { \ InitCall *initcall = (InitCall *)(MODULE_BEGIN(name, step)); \ InitCall *initend = (InitCall *)(MODULE_END(name, step)); \ for (; initcall < initend; initcall++) { \ printf(“[core_main] call pointer address: %p\n”, initcall); \ printf(“[core_main] func address: %p\n”, *initcall); \ (*initcall)(); \ } \ } while (0) #define MODULE_BEGIN(name, step) \ ({ extern InitCall __zinitcall_##name##_start; \ InitCall *initCall = &__zinitcall_##name##_start; \ (initCall); \ }) #define MODULE_END(name, step) \ ({ extern InitCall __zinitcall_##name##_end; \ InitCall *initCall = &__zinitcall_##name##_end; \ (initCall); \ })
<

不知道大家看明白没有,MODULE_INIT(name)的意思就是在遍历从.zinitcall_name_start到.zinitcall_name_end这一段区域中保存的函数指针 ,然后挨个调用一遍。

how?

那么我们来捋一下。真相就是:

HOS_SystemInit()执行过程中,去在.zinitcall_run_start和.zinitcall_run_start之间的.zinitcall.run2.init程序段中找到了保存的Init_Trace函数对应的函数指针,然后调用了他。 Now,show you the 证据, 老套路 out/wifiiot/Hi3861_wifiiot_app.asm与out/wifiiot/Hi3861_wifiiot_app.map。

004a0810 <HOS_SystemInit>: 4a0810: 81a5a2ef jal t0,3fa82a <__riscv_save_4> 4a0814: 00004497 auipc s1,0x4 4a0818: 77c4a483 lw s1,1916(s1) # 4a4f90 <_GLOBAL_OFFSET_TABLE_+0xc4> 4a081c: 409c lw a5,0(s1) 4a081e: 1141 addi sp,sp,-16 4a0820: 0000e517 auipc a0,0xe 4a0824: da850513 addi a0,a0,-600 # 4ae5c8 <cb.8884+0x2ac> 4a0828: c63e sw a5,12(sp) 4a082a: 00004417 auipc s0,0x4 4a082e: 77a42403 lw s0,1914(s0) # 4a4fa4 <_GLOBAL_OFFSET_TABLE_+0xd8> 4a0832: c7b590ef jal ra,3fa4ac <printf> 4a0836: 00004917 auipc s2,0x4 4a083a: 6fe92903 lw s2,1790(s2) # 4a4f34 <_GLOBAL_OFFSET_TABLE_+0x68> 4a083e: 0000e997 auipc s3,0xe 4a0842: da298993 addi s3,s3,-606 # 4ae5e0 <cb.8884+0x2c4> 4a0846: 0000ea17 auipc s4,0xe 4a084a: dc2a0a13 addi s4,s4,-574 # 4ae608 <cb.8884+0x2ec> 4a084e: 0b246a63 bltu s0,s2,4a0902 <HOS_SystemInit+0xf2> 4a0852: 00004417 auipc s0,0x4 4a0856: 78242403 lw s0,1922(s0) # 4a4fd4 <_GLOBAL_OFFSET_TABLE_+0x108> 4a085a: 00004917 auipc s2,0x4 4a085e: 72692903 lw s2,1830(s2) # 4a4f80 <_GLOBAL_OFFSET_TABLE_+0xb4> 4a0862: 0000e997 auipc s3,0xe 4a0866: d7e98993 addi s3,s3,-642 # 4ae5e0 <cb.8884+0x2c4> 4a086a: 0000ea17 auipc s4,0xe 4a086e: d9ea0a13 addi s4,s4,-610 # 4ae608 <cb.8884+0x2ec> 4a0872: 0b246563 bltu s0,s2,4a091c <HOS_SystemInit+0x10c> 4a0876: 00004417 auipc s0,0x4 4a087a: 7c242403 lw s0,1986(s0) # 4a5038 <_GLOBAL_OFFSET_TABLE_+0x16c> 4a087e: 00004917 auipc s2,0x4 4a0882: 6f692903 lw s2,1782(s2) # 4a4f74 <_GLOBAL_OFFSET_TABLE_+0xa8> 4a0886: 0000e997 auipc s3,0xe 4a088a: d5a98993 addi s3,s3,-678 # 4ae5e0 <cb.8884+0x2c4> 4a088e: 0000ea17 auipc s4,0xe 4a0892: d7aa0a13 addi s4,s4,-646 # 4ae608 <cb.8884+0x2ec> 4a0896: 0b246063 bltu s0,s2,4a0936 <HOS_SystemInit+0x126> 4a089a: 00004417 auipc s0,0x4 4a089e: 65642403 lw s0,1622(s0) # 4a4ef0 <_GLOBAL_OFFSET_TABLE_+0x24> 4a08a2: 00004917 auipc s2,0x4 4a08a6: 7aa92903 lw s2,1962(s2) # 4a504c <_GLOBAL_OFFSET_TABLE_+0x180> 4a08aa: 0b246363 bltu s0,s2,4a0950 <HOS_SystemInit+0x140> 4a08ae: 00004417 auipc s0,0x4 4a08b2: 71e42403 lw s0,1822(s0) # 4a4fcc <_GLOBAL_OFFSET_TABLE_+0x100> 4a08b6: 00004917 auipc s2,0x4 4a08ba: 6d292903 lw s2,1746(s2) # 4a4f88 <_GLOBAL_OFFSET_TABLE_+0xbc> 4a08be: 09246d63 bltu s0,s2,4a0958 <HOS_SystemInit+0x148> 4a08c2: 00004417 auipc s0,0x4 4a08c6: 65642403 lw s0,1622(s0) # 4a4f18 <_GLOBAL_OFFSET_TABLE_+0x4c> 4a08ca: 00004917 auipc s2,0x4 4a08ce: 76a92903 lw s2,1898(s2) # 4a5034 <_GLOBAL_OFFSET_TABLE_+0x168> 4a08d2: 0000e997 auipc s3,0xe 4a08d6: d0e98993 addi s3,s3,-754 # 4ae5e0 <cb.8884+0x2c4> 4a08da: 0000ea17 auipc s4,0xe 4a08de: d2ea0a13 addi s4,s4,-722 # 4ae608 <cb.8884+0x2ec> 4a08e2: 07246f63 bltu s0,s2,4a0960 <HOS_SystemInit+0x150> 4a08e6: 0000e517 auipc a0,0xe 4a08ea: d4250513 addi a0,a0,-702 # 4ae628 <cb.8884+0x30c> 4a08ee: bbf590ef jal ra,3fa4ac <printf> 4a08f2: 6a9020ef jal ra,4a379a <SAMGR_Bootstrap> 4a08f6: 4732 lw a4,12(sp) 4a08f8: 409c lw a5,0(s1) 4a08fa: 08f70063 beq a4,a5,4a097a <HOS_SystemInit+0x16a> 4a08fe: 8bb550ef jal ra,3f61b8 <__stack_chk_fail> 4a0902: 85a2 mv a1,s0 4a0904: 854e mv a0,s3 4a0906: ba7590ef jal ra,3fa4ac <printf> 4a090a: 400c lw a1,0(s0) 4a090c: 8552 mv a0,s4 4a090e: 0411 addi s0,s0,4 4a0910: b9d590ef jal ra,3fa4ac <printf> 4a0914: ffc42783 lw a5,-4(s0) 4a0918: 9782 jalr a5 4a091a: bf15 j 4a084e <HOS_SystemInit+0x3e> 4a091c: 85a2 mv a1,s0 4a091e: 854e mv a0,s3 4a0920: b8d590ef jal ra,3fa4ac <printf> 4a0924: 400c lw a1,0(s0) 4a0926: 8552 mv a0,s4 4a0928: 0411 addi s0,s0,4 4a092a: b83590ef jal ra,3fa4ac <printf> 4a092e: ffc42783 lw a5,-4(s0) 4a0932: 9782 jalr a5 4a0934: bf3d j 4a0872 <HOS_SystemInit+0x62> 4a0936: 85a2 mv a1,s0 4a0938: 854e mv a0,s3 4a093a: b73590ef jal ra,3fa4ac <printf> 4a093e: 400c lw a1,0(s0) 4a0940: 8552 mv a0,s4 4a0942: 0411 addi s0,s0,4 4a0944: b69590ef jal ra,3fa4ac <printf> 4a0948: ffc42783 lw a5,-4(s0) 4a094c: 9782 jalr a5 4a094e: b7a1 j 4a0896 <HOS_SystemInit+0x86> 4a0950: 401c lw a5,0(s0) 4a0952: 0411 addi s0,s0,4 4a0954: 9782 jalr a5 4a0956: bf91 j 4a08aa <HOS_SystemInit+0x9a> 4a0958: 401c lw a5,0(s0) 4a095a: 0411 addi s0,s0,4 4a095c: 9782 jalr a5 4a095e: b785 j 4a08be <HOS_SystemInit+0xae> 4a0960: 85a2 mv a1,s0 4a0962: 854e mv a0,s3 4a0964: b49590ef jal ra,3fa4ac <printf> 4a0968: 400c lw a1,0(s0) 4a096a: 8552 mv a0,s4 4a096c: 0411 addi s0,s0,4 4a096e: b3f590ef jal ra,3fa4ac <printf> 4a0972: ffc42783 lw a5,-4(s0) 4a0976: 9782 jalr a5 4a0978: b7ad j 4a08e2 <HOS_SystemInit+0xd2> 4a097a: 0141 addi sp,sp,16 4a097c: ee35906f j 3fa85e <__riscv_restore_4>
<

上面这一段就是HOS_SystemInit的整个汇编实现了,是不是感觉看不懂,没关系,小A也不怎么懂,但是不妨碍我们找到我们想要的。

[HarmonyOS之旅]  Chapter5 - 有趣的init

红框中就是我们想要的,根据源码HOS_SystemInit,两个printf之间,夹了6条语句。途中标号1-6分别就是这6条语句执行完后的汇编回跳语句。我们想确认的MODULE_INIT(run);是第六条,因此我们从5号位后面开始看起,到6号位结束。

4a08c6: 65642403 lw s0,1622(s0) # 4a4f18 <_GLOBAL_OFFSET_TABLE_+0x4c> 4a08ca: 00004917 auipc s2,0x4 4a08ce: 76a92903 lw s2,1898(s2) # 4a5034 <_GLOBAL_OFFSET_TABLE_+0x168> 4a08d2: 0000e997 auipc s3,0xe 4a08d6: d0e98993 addi s3,s3,-754 # 4ae5e0 <cb.8884+0x2c4> 4a08da: 0000ea17 auipc s4,0xe 4a08de: d2ea0a13 addi s4,s4,-722 # 4ae608 <cb.8884+0x2ec> 4a08e2: 07246f63 bltu s0,s2,4a0960 <HOS_SystemInit+0x150>

这里的第一条是访址寻址的意思,至于这个地址是什么,很明显

_GLOBAL_OFFSET_TABLE_+0x4c

[HarmonyOS之旅]  Chapter5 - 有趣的init

知道了这个地址,那它是什么呢,我们再深入一点

[HarmonyOS之旅]  Chapter5 - 有趣的init

看到这里就很明显了,是不是差不多可以证明实际上就是HOS_SystemInit函数体里“调用”到了我们的测试函数。真的是这样吗?我们继续看。这里去码揭晓真相。

[HarmonyOS之旅]  Chapter5 - 有趣的init

[HarmonyOS之旅]  Chapter5 - 有趣的init

看到了吗,HOS_SystemInit真的去了4aeb5c这个地址,然后把保存的函数指针(4a07e0)取了出来,并执行。结题,撒花。

总结:

​ 这种初始化方式是不是很有趣呢。其实熟悉linux驱动的话,大家就会发现这个机制和linux的initcall机制是一样的。这种通过构建初始化函数表的方式来完成初始化,简化了代码的耦合性并降低了代码的污染度,真的是一种很巧妙的设计。

​ 另外这种机制也是分优先级等级的,具体的优先级顺序,留给大家去探索吧。😀

​ 下回见~

免责声明:文章内容来自互联网,本站不对其真实性负责,也不承担任何法律责任,如有侵权等情况,请与本站联系删除。
转载请注明出处:[HarmonyOS之旅] Chapter5 – 有趣的init https://www.yhzz.com.cn/a/13358.html

上一篇 2023-05-11 21:22:36
下一篇 2023-05-11 21:25:51

相关推荐

联系云恒

在线留言: 我要留言
客服热线:400-600-0310
工作时间:周一至周六,08:30-17:30,节假日休息。