自动初始化机制是指初始化函数不需要被显式调用,只需要在函数定义处通过宏定义的方式进行申明,就会在系统启动过程中被执行。这篇文章就来探索一下其中的奥秘, 简单理解其原理!
|知识点补充
__attribute__((section(x)))是GNU C的一个特色之一,它可以用于将变量或函数放置在指定的段中。例如,你可以使用__attribute__((section(".my_section")))将变量或函数放置在名为my_section的段中。这对于嵌入式系统编程和操作系统内核编程非常有用。
__attribute__((used))是GCC编译器提供的一个特性,用于告诉编译器在目标文件中保留一个静态变量或函数,即使它没有被引用。这样可以避免链接器删除未使用的节,或者确保某些特定的变量或函数被输出。
__attribute__((unused))是GCC编译器提供的一个特性,用于告诉编译器某个变量或函数可能未被使用,从而避免编译器产生未使用变量或函数的警告。在变量或函数前加上__attribute__((unused))即可使用该特性。
__attribute__((aligned(n)))是GCC编译器提供的一个特性,用于设置变量、类型、函数的对齐方式。它的作用是告诉编译器在分配内存空间时,要求以n个字节为边界。
__attribute__((weak))是GCC编译器提供的一个特性,用于声明或定义一个弱符号(weak symbol)。弱符号是指在链接时,如果存在同名的强符号(strong symbol),则会被强符号覆盖。
| 原理研究
深入研究了一下, 发现这样使用宏真的很奇妙, 这里就简单介绍一下原理:
export.h文件
#ifndef__EXPORT_H #define__EXPORT_H #defineEXPORT_USED__attribute__((used)) #defineEXPORT_SECTION(x)__attribute__((section(x))) typedefint(*export_init_fn_t)(void); #defineEXPORT_INIT_EXPORT(fn,level) EXPORT_USEDconstexport_init_fn_t__export_call_##fnEXPORT_SECTION(".export_call."level)=fn //板级初始化顺序1 #defineEXPORT_BOARD_INIT(fn)EXPORT_INIT_EXPORT(fn,"1") //设备初始化顺序3 #defineEXPORT_DEVICE_INIT(fn)EXPORT_INIT_EXPORT(fn,"2") //组件初始化顺序4 #defineEXPORT_COMPONENT_INIT(fn)EXPORT_INIT_EXPORT(fn,"3") //环境初始化顺序5 #defineEXPORT_ENV_INIT(fn)EXPORT_INIT_EXPORT(fn,"4") //APP初始化顺序6 #defineEXPORT_APP_INIT(fn)EXPORT_INIT_EXPORT(fn,"5") voidexport_components_init(void); #endif
export.c文件
#include"export.h" #include"stdio.h" staticinttest_0_start(void) { return0; } EXPORT_INIT_EXPORT(test_0_start,"0"); staticinttest_0_0(void) { return0; } EXPORT_INIT_EXPORT(test_0_0,"0"); staticinttest_0_1(void) { return0; } EXPORT_INIT_EXPORT(test_0_1,"0"); staticinttest_0_end(void) { return0; } EXPORT_INIT_EXPORT(test_0_end,"0.end"); staticinttest_1_start(void) { return0; } EXPORT_INIT_EXPORT(test_1_start,"1"); staticinttest_1_0(void) { return0; } EXPORT_INIT_EXPORT(test_1_0,"1"); staticinttest_1_1(void) { return0; } EXPORT_INIT_EXPORT(test_1_1,"1"); staticinttest_1_end(void) { return0; } EXPORT_INIT_EXPORT(test_1_end,"1.end"); //自动初始化(在main函数调用) voidexport_components_init(void) { printf("pfn1:%p ",&__export_call_test_0_start); printf("pfn2:%p ",&__export_call_test_0_0); printf("pfn3:%p ",&__export_call_test_0_1); printf("pfn4:%p ",&__export_call_test_0_end); printf("pfn5:%p ",&__export_call_test_1_start); printf("pfn6:%p ",&__export_call_test_1_0); printf("pfn7:%p ",&__export_call_test_1_1); printf("pfn8:%p ",&__export_call_test_1_end); volatileconstexport_init_fn_t*pfn; for(pfn=&__export_call_test_0_start;pfn< &__export_call_test_1_end; pfn++) { printf("%p ", pfn); // (*pfn)(); } }
结果输出:
pfn1:08000c50 pfn2:08000c54 pfn3:08000c58 pfn4:08000c5c pfn5:08000c60 pfn6:08000c64 pfn7:08000c68 pfn8:08000c6c 08000c50 08000c54 08000c58 08000c5c 08000c60 08000c64 08000c68
过程分析:
这个测试代码片段主要定义和使用了两个段,每个段定义了开始和结束,并且在开始和结束间插入了若干个函数,通过观察地址的变化会发现, 它们是按规律递增的,就可以使用遍历来调用指针指向的函数, 从而实现自动初始化外设的目的.
细节分析:
//定义一个函数指针 typedefint(*export_init_fn_t)(void); //宏定义 #defineEXPORT_INIT_EXPORT(fn,level) EXPORT_USEDconstexport_init_fn_t__export_call_##fnEXPORT_SECTION(".export_call."level)=fn //假设调用 EXPORT_INIT_EXPORT(test_1_0,"1"); //一顿操作后,内存就存在了一个export_init_fn_t__export_call_test_1_0存放在".export_call."的输入段中,并指定其属于第一级初始化段 //就可以通过指针调用指针指向的函数来调用指定的函数,实现自动化初始化
|EventOS的EXPORT
这个先待定, 后续有时间再移植,export需要参考elab,涉及到assertcommonexportlog,感兴趣的读者可以参考:
链接//gitee.com/event-os/eventos/tree/dev_df/examples/stm32g070
使用了export机制可以让代码变得更加简洁,感兴趣的读者可以在理解原理后进行完善和优化.
审核编辑:汤梓红
-
GCC
+关注
关注
0文章
107浏览量
24835 -
开源
+关注
关注
3文章
3309浏览量
42471 -
函数
+关注
关注
3文章
4327浏览量
62569 -
编译器
+关注
关注
1文章
1623浏览量
49107 -
宏定义
+关注
关注
0文章
50浏览量
9003
原文标题:开源探索|EventOS之自动初始化
文章出处:【微信号:玩转单片机,微信公众号:玩转单片机】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论