自动初始化机制原理详解

自动初始化机制是指初始化函数不需要被显式调用,只需要在函数定义处通过宏定义的方式进行申明,就会在系统启动过程中被执行。这篇文章就来探索一下其中的奥秘, 简单理解其原理!
| 知识点补充
__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#define export_used        __attribute__((used))#define export_section(x)  __attribute__((section(x)))typedef int (*export_init_fn_t)(void);#define export_init_export(fn,level)     export_used const export_init_fn_t __export_call_##fn export_section(.export_call. level) = fn//板级初始化 顺序1#define export_board_init(fn)      export_init_export(fn,1)//设备初始化 顺序3#define export_device_init(fn)     export_init_export(fn,2)//组件初始化 顺序4#define export_component_init(fn)  export_init_export(fn,3)//环境初始化 顺序5#define export_env_init(fn)        export_init_export(fn,4)//app初始化 顺序6#define export_app_init(fn)        export_init_export(fn,5)void export_components_init(void);#endif  
export.c文件
#include export.h#include stdio.hstatic int test_0_start(void){    return 0;}export_init_export(test_0_start,0);static int test_0_0(void){    return 0;}export_init_export(test_0_0,0);static int test_0_1(void){    return 0;}export_init_export(test_0_1,0);static int test_0_end(void){    return 0;}export_init_export(test_0_end,0.end);static int test_1_start(void){    return 0;}export_init_export(test_1_start,1);static int test_1_0(void){    return 0;}export_init_export(test_1_0,1);static int test_1_1(void){    return 0;}export_init_export(test_1_1,1);static int test_1_end(void){    return 0;}export_init_export(test_1_end,1.end);// 自动初始化(在main函数调用)void export_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);    volatile const export_init_fn_t *pfn;    for(pfn = &__export_call_test_0_start; pfn < &__export_call_test_1_end; pfn++)    {        printf(%p, pfn);        // (*pfn)();    }}  
结果输出:
pfn1:08000c50pfn2:08000c54pfn3:08000c58pfn4:08000c5cpfn5:08000c60pfn6:08000c64pfn7:08000c68pfn8:08000c6c08000c5008000c5408000c5808000c5c08000c6008000c6408000c68  
过程分析:
这个测试代码片段主要定义和使用了两个段, 每个段定义了开始和结束, 并且在开始和结束间插入了若干个函数, 通过观察地址的变化会发现, 它们是按规律递增的, 就可以使用遍历来调用指针指向的函数, 从而实现自动初始化外设的目的.
细节分析:
// 定义一个函数指针typedef int (*export_init_fn_t)(void);// 宏定义#define export_init_export(fn,level)     export_used const export_init_fn_t __export_call_##fn export_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机制可以让代码变得更加简洁, 感兴趣的读者可以在理解原理后进行完善和优化.


在多方权衡之下,Google 在中国未来是否还有更好的出路?
联创电子获传音控股2000万部订单 未来预计承接5000万部
电话录音设备,USB电话录音器,数码电话录音,模拟电话录音
采用嵌入式设计和现代电子测量技术实现便携式数字存储示波表的设计
低温振荡水槽产品特点的简单介绍
自动初始化机制原理详解
怎样用ESP32主板制作一个带有3.5英寸显示屏的网络收音机设备
简单、实用的高低音电路
LG Display OLED面板工厂失火 面板线停产并损失超百万美金
Chrome新版本上线 修复数据清空问题
Qualcomm董事长保罗•雅各布博士:LTE推动创新
传感技术的重要性主要体现在哪些方面
油品检测仪的优点介绍
倍加福R200和R201光电传感器,更长的检测距离也可以实现
海盗船新款RGB风扇在国内上架,搭载34个可单独编程的LED
关于持续集成与基于模型的设计的分析和应用
某微电子有限公司电力监控系统的设计以及应用
QuantumScape发布其2021年第一季度业绩报告
应用互联网技术的电动自行车智能充电平台
传感器选用方法