| 前情回顾
参考HarmonyOS的文档,我们写了第一个应用程序。
void Init_Trace(void)
{printf("[codinglab] >>>> Hello HarmonyOSn");
};
SYS_RUN(Init_Trace);
并添加了两句打印
void HOS_SystemInit(void)
{printf("[codinglab] >>>> startn");MODULE_INIT(bsp);MODULE_INIT(device);MODULE_INIT(core);SYS_INIT(service);SYS_INIT(feature);MODULE_INIT(run);printf("[codinglab] >>>> endn");SAMGR_Bootstrap();
}
得到的结果是这样的。
| 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__((p(".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__((p("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: %pn", initcall); printf("[core_main] func address: %pn", *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,0x44a0818: 77c4a483 lw s1,1916(s1) # 4a4f90 <_GLOBAL_OFFSET_TABLE_+0xc4>4a081c: 409c lw a5,0(s1)4a081e: 1141 addi sp,sp,-164a0820: 0000e517 auipc a0,0xe4a0824: da850513 addi a0,a0,-600 # 4ae5c8 <cb.8884+0x2ac>4a0828: c63e sw a5,12(sp)4a082a: 00004417 auipc s0,0x44a082e: 77a42403 lw s0,1914(s0) # 4a4fa4 <_GLOBAL_OFFSET_TABLE_+0xd8>4a0832: c7b590ef jal ra,3fa4ac <printf>4a0836: 00004917 auipc s2,0x44a083a: 6fe92903 lw s2,1790(s2) # 4a4f34 <_GLOBAL_OFFSET_TABLE_+0x68>4a083e: 0000e997 auipc s3,0xe4a0842: da298993 addi s3,s3,-606 # 4ae5e0 <cb.8884+0x2c4>4a0846: 0000ea17 auipc s4,0xe4a084a: dc2a0a13 addi s4,s4,-574 # 4ae608 <cb.8884+0x2ec>4a084e: 0b246a63 bltu s0,s2,4a0902 <HOS_SystemInit+0xf2>4a0852: 00004417 auipc s0,0x44a0856: 78242403 lw s0,1922(s0) # 4a4fd4 <_GLOBAL_OFFSET_TABLE_+0x108>4a085a: 00004917 auipc s2,0x44a085e: 72692903 lw s2,1830(s2) # 4a4f80 <_GLOBAL_OFFSET_TABLE_+0xb4>4a0862: 0000e997 auipc s3,0xe4a0866: d7e98993 addi s3,s3,-642 # 4ae5e0 <cb.8884+0x2c4>4a086a: 0000ea17 auipc s4,0xe4a086e: d9ea0a13 addi s4,s4,-610 # 4ae608 <cb.8884+0x2ec>4a0872: 0b246563 bltu s0,s2,4a091c <HOS_SystemInit+0x10c>4a0876: 00004417 auipc s0,0x44a087a: 7c242403 lw s0,1986(s0) # 4a5038 <_GLOBAL_OFFSET_TABLE_+0x16c>4a087e: 00004917 auipc s2,0x44a0882: 6f692903 lw s2,1782(s2) # 4a4f74 <_GLOBAL_OFFSET_TABLE_+0xa8>4a0886: 0000e997 auipc s3,0xe4a088a: d5a98993 addi s3,s3,-678 # 4ae5e0 <cb.8884+0x2c4>4a088e: 0000ea17 auipc s4,0xe4a0892: d7aa0a13 addi s4,s4,-646 # 4ae608 <cb.8884+0x2ec>4a0896: 0b246063 bltu s0,s2,4a0936 <HOS_SystemInit+0x126>4a089a: 00004417 auipc s0,0x44a089e: 65642403 lw s0,1622(s0) # 4a4ef0 <_GLOBAL_OFFSET_TABLE_+0x24>4a08a2: 00004917 auipc s2,0x44a08a6: 7aa92903 lw s2,1962(s2) # 4a504c <_GLOBAL_OFFSET_TABLE_+0x180>4a08aa: 0b246363 bltu s0,s2,4a0950 <HOS_SystemInit+0x140>4a08ae: 00004417 auipc s0,0x44a08b2: 71e42403 lw s0,1822(s0) # 4a4fcc <_GLOBAL_OFFSET_TABLE_+0x100>4a08b6: 00004917 auipc s2,0x44a08ba: 6d292903 lw s2,1746(s2) # 4a4f88 <_GLOBAL_OFFSET_TABLE_+0xbc>4a08be: 09246d63 bltu s0,s2,4a0958 <HOS_SystemInit+0x148>4a08c2: 00004417 auipc s0,0x44a08c6: 65642403 lw s0,1622(s0) # 4a4f18 <_GLOBAL_OFFSET_TABLE_+0x4c>4a08ca: 00004917 auipc s2,0x44a08ce: 76a92903 lw s2,1898(s2) # 4a5034 <_GLOBAL_OFFSET_TABLE_+0x168>4a08d2: 0000e997 auipc s3,0xe4a08d6: d0e98993 addi s3,s3,-754 # 4ae5e0 <cb.8884+0x2c4>4a08da: 0000ea17 auipc s4,0xe4a08de: d2ea0a13 addi s4,s4,-722 # 4ae608 <cb.8884+0x2ec>4a08e2: 07246f63 bltu s0,s2,4a0960 <HOS_SystemInit+0x150>4a08e6: 0000e517 auipc a0,0xe4a08ea: 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,s04a0904: 854e mv a0,s34a0906: ba7590ef jal ra,3fa4ac <printf>4a090a: 400c lw a1,0(s0)4a090c: 8552 mv a0,s44a090e: 0411 addi s0,s0,44a0910: b9d590ef jal ra,3fa4ac <printf>4a0914: ffc42783 lw a5,-4(s0)4a0918: 9782 jalr a54a091a: bf15 j 4a084e <HOS_SystemInit+0x3e>4a091c: 85a2 mv a1,s04a091e: 854e mv a0,s34a0920: b8d590ef jal ra,3fa4ac <printf>4a0924: 400c lw a1,0(s0)4a0926: 8552 mv a0,s44a0928: 0411 addi s0,s0,44a092a: b83590ef jal ra,3fa4ac <printf>4a092e: ffc42783 lw a5,-4(s0)4a0932: 9782 jalr a54a0934: bf3d j 4a0872 <HOS_SystemInit+0x62>4a0936: 85a2 mv a1,s04a0938: 854e mv a0,s34a093a: b73590ef jal ra,3fa4ac <printf>4a093e: 400c lw a1,0(s0)4a0940: 8552 mv a0,s44a0942: 0411 addi s0,s0,44a0944: b69590ef jal ra,3fa4ac <printf>4a0948: ffc42783 lw a5,-4(s0)4a094c: 9782 jalr a54a094e: b7a1 j 4a0896 <HOS_SystemInit+0x86>4a0950: 401c lw a5,0(s0)4a0952: 0411 addi s0,s0,44a0954: 9782 jalr a54a0956: bf91 j 4a08aa <HOS_SystemInit+0x9a>4a0958: 401c lw a5,0(s0)4a095a: 0411 addi s0,s0,44a095c: 9782 jalr a54a095e: b785 j 4a08be <HOS_SystemInit+0xae>4a0960: 85a2 mv a1,s04a0962: 854e mv a0,s34a0964: b49590ef jal ra,3fa4ac <printf>4a0968: 400c lw a1,0(s0)4a096a: 8552 mv a0,s44a096c: 0411 addi s0,s0,44a096e: b3f590ef jal ra,3fa4ac <printf>4a0972: ffc42783 lw a5,-4(s0)4a0976: 9782 jalr a54a0978: b7ad j 4a08e2 <HOS_SystemInit+0xd2>4a097a: 0141 addi sp,sp,164a097c: ee35906f j 3fa85e <__riscv_restore_4>
上面这一段就是HOS_SystemInit的整个汇编实现了,是不是感觉看不懂,没关系,小A也不怎么懂,但是不妨碍我们找到我们想要的。
红框中就是我们想要的,根据源码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,0x44a08ce: 76a92903 lw s2,1898(s2) # 4a5034 <_GLOBAL_OFFSET_TABLE_+0x168>4a08d2: 0000e997 auipc s3,0xe4a08d6: d0e98993 addi s3,s3,-754 # 4ae5e0 <cb.8884+0x2c4>4a08da: 0000ea17 auipc s4,0xe4a08de: d2ea0a13 addi s4,s4,-722 # 4ae608 <cb.8884+0x2ec>4a08e2: 07246f63 bltu s0,s2,4a0960 <HOS_SystemInit+0x150>
这里的第一条是访址寻址的意思,至于这个地址是什么,很明显
_GLOBAL_OFFSET_TABLE_+0x4c
知道了这个地址,那它是什么呢,我们再深入一点
看到这里就很明显了,是不是差不多可以证明实际上就是 HOS_SystemInit 函数体里“调用”到了我们的测试函数。真的是这样吗?我们继续看。这里去码揭晓真相。
看到了吗,HOS_SystemInit真的去了4aeb5c这个地址,然后把保存的函数指针(4a07e0)取了出来,并执行。结题,撒花。
| 总结
这种初始化方式是不是很有趣呢。其实熟悉linux驱动的话,大家就会发现这个机制和linux的initcall机制是一样的。这种通过构建初始化函数表的方式来完成初始化,简化了代码的耦合性并降低了代码的污染度,真的是一种很巧妙的设计。
另外这种机制也是分优先级等级的,具体的优先级顺序,留给大家去探索吧。????
下回见~
鸿蒙OS开源代码精要解读之init 电子书
公众号后台回复:init ,获取下载链接
关注公众号,及时获取资料更新通知
本文发布于:2024-01-30 16:18:46,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170660272821279.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |