内核启动函数

#define app_run(func) layer_initcall_def(func, run, "app_run")

#define layer_initcall_def(func, layer, clayer) \

    layer_initcall(func, layer, clayer, 0)

#define layer_initcall(func, layer, clayer, priority)            \

    static const init_call_t USED_ATTR __zinitcall_##layer##_##func \

        __attribute__((section(".zinitcall." clayer #priority ".init"))) = (func)


代码逐行拆解:内核/RTOS 模块自动初始化注册机制(类Linux initcall 分段初始化)

整套宏作用:把应用函数func按分类+优先级放到指定段,系统启动时统一遍历段自动调用,不用手动逐个调用函数。

1. 顶层调用宏

#define app_run(func) layer_initcall_def(func, run, "app_run")

app_run(xxx_func)等价展开:

layer_initcall_def(xxx_func, run, "app_run")

• 用途:标记一个应用入口函数属于 run 分类、分类名字符串app_run

• 业务含义:所有用app_run(函数名)修饰的函数,归类为APP运行初始化函数。

2. 中间转接宏

#define layer_initcall_def(func, layer, clayer) \

    layer_initcall(func, layer, clayer, 0)

补充默认优先级=0,转接到底层定义宏:

layer_initcall_def(func,run,"app_run") → layer_initcall(func, run, "app_run", 0)

3. 底层核心定义宏(关键)

#define layer_initcall(func, layer, clayer, priority)            \

static const init_call_t USED_ATTR __zinitcall_##layer##_##func \

__attribute__((section(".zinitcall." clayer #priority ".init"))) = (func)

3.1 符号拼接 ## 令牌粘合

##layer##_##func:拼接变量名

示例:layer=run、func=test_app

生成变量名:__zinitcall_run_test_app

3.2 变量类型与修饰

• init_call_t:函数指针类型,原型:typedef void (*init_call_t)(void);,存初始化函数地址

• static const:只读静态全局,防止被优化删除

• USED_ATTR:一般是__attribute__((used)),禁止编译器未引用优化删掉该变量(关键:即使没代码引用变量,也保留在段里)

3.3 __attribute__((section("xxx"))) 自定义段(核心原理)

section(".zinitcall." clayer #priority ".init")

• #priority:#字符串化,数字变字符串

priority=0 → "0"

• clayer="app_run"

最终拼接段名:

.zinitcall.app_run0.init

示例:app_run(test_app)展开后完整代码:

static const init_call_t __zinitcall_run_test_app

__attribute__((section(".zinitcall.app_run0.init"))) = test_app;

含义:定义一个全局函数指针变量,把test_app函数地址存入自定义段.zinitcall.app_run0.init

4. 链接脚本 + 启动调用逻辑(配套隐含逻辑)

1. 链接脚本lds:把所有同名.zinitcall.app_runX.init段连续排布在内存固定区域;同分类不同priority(X数字)按段名排序,数字越小优先级越高,先执行。

2. 系统启动代码:

在启动初始化阶段,从链接脚本获取 .zinitcall.app_run0.init 段的起始地址、结束地址,循环遍历段内所有init_call_t函数指针,逐个调用:

// 伪代码启动遍历

extern init_call_t __zinit_app_run_start[], __zinit_app_run_end[];

for(int i=0; __zinit_app_run_start+i < __zinit_app_run_end; i++){

    __zinit_app_run_start[i](); // 自动执行所有app_run注册的函数

}

5. 使用示例

// 定义应用初始化函数

void app_led_init(void){ /*LED初始化*/ }

void app_key_init(void){ /*按键初始化*/ }

// 注册到自动初始化

app_run(app_led_init);

app_run(app_key_init);

编译后:

app_led_init、app_key_init 的地址被放进 .zinitcall.app_run0.init 段,系统上电自动执行两个函数,无需手动调用。

6. 扩展特性

• 改priority参数即可调整执行顺序:

封装app_run_p1(func) layer_initcall_def(func,run,"app_run",1),段名变为.zinitcall.app_run1.init,优先级低于0

• 不同分类:可新增app_board(func),修改clayer="board",段变成.zinitcall.board0.init,分批次初始化:板级初始化→APP初始化。

总结

这套是嵌入式/RTOS通用组件自动化初始化框架,

不用在main里挨个调用初始化函数,通过宏注册+自定义段+链接排布,实现组件解耦、按需注册、按优先级自动初始化。和Linux内核module_init、__initcall实现思路一模一样。

__attribute__ 详解(GCC 编译器扩展语法,只用于GCC/ARM-GCC,标准C没有)

1、作用总述

__attribute__((xxx)):给变量/函数/结构体附加编译器特殊属性指令,告诉编译器:存储位置、优化规则、对齐、占用段、是否丢弃等。

格式:

__attribute__((属性1,属性2,...))

2、你代码里用到的两个属性

① __attribute__((section("段名")))

__attribute__((section(".zinitcall.app_run0.init")))

• 默认:全局变量放 .data / .rodata

• 加 section:强制把这个变量放到自定义段

你的初始化指针全部归集在同一个自定义段,链接脚本统一收拢,启动时遍历整段自动调用,就是这套初始化框架的核心。

② __attribute__((used))(对应宏 USED_ATTR)

• 不加:变量没有代码直接引用,编译器优化裁剪删掉

• used:告诉编译器:即便没代码引用,也保留目标文件符号,不被优化删除

初始化变量只有链接脚本使用,C代码从不直接写名字调用,必须加used防止被优化丢段。

3、嵌入式常用其他attribute

1. __attribute__((packed)):结构体取消对齐,紧凑排布(串口协议、寄存器结构体常用)

typedef struct{char a;int b;} __attribute__((packed)) test;

2. __attribute__((aligned(N))):指定N字节对齐

3. __attribute__((noreturn)):函数永不返回(如死机死循环函数)

4. __attribute__((weak)):弱符号,可被同名函数覆盖(驱动分层常用)

4、补充要点

1. 双下划线 __attribute__ 是GCC专属,MDK(ARMCC)用 __attribute / __at,IAR语法不同。

2. 只在编译阶段生效,不改变C语言语法逻辑,是编译器指令。

结合你源码一句话总结

__attribute__((section("xxx")))

= 指定存放段

USED_ATTR=__attribute__((used))

= 禁止编译器优化删除

最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容