在系统函数查找系统的启动方式时,系统启动使用函数指针将所有初始化的函数按照指针函数一个个顺序执行。
#define SYS_CALL(name, step) \
do { \
InitCall *initcall = (InitCall *)(SYS_BEGIN(name, step)); \
InitCall *initend = (InitCall *)(SYS_END(name, step)); \
for (; initcall < initend; initcall++) { \
(*initcall)(); \
} \
} while (0)
其中就有获取参函数开始与结束。
#define SYS_BEGIN(name, step) \
({ extern InitCall __zinitcall_sys_##name##_start; \
InitCall *initCall = &__zinitcall_sys_##name##_start; \
(initCall); \
})
#define SYS_END(name, step) \
({ extern InitCall __zinitcall_sys_##name##_end; \
InitCall *initCall = &__zinitcall_sys_##name##_end; \
(initCall); \
})
这段代码是用来定义两个宏 SYS_BEGIN 和 SYS_END,它们用于获取系统初始化调用的开始和结束位置。让我们逐步分解这段代码:
宏 SYS_BEGIN(name, step):
功能:这个宏用于获取名为 _zinitcall_sys##name##_start 的全局变量的地址。## 是C语言中的字符串连接运算符,用于将宏参数与其他字符串连接。
实现细节:
extern InitCall _zinitcall_sys##name##_start;:这一行声明了一个外部变量,变量的名称根据传入的 name 动态生成。这里的 InitCall 是一种函数指针类型,通常用于指向初始化函数。
InitCall *initCall = &_zinitcall_sys##name##_start;:这一行将上述外部变量的地址赋值给指针变量 initCall。
return (initCall);:最终,宏返回这个指针。
宏 SYS_END(name, step):
功能:和 SYS_BEGIN 一样,这个宏用于获取名为 _zinitcall_sys##name##_end 的全局变量的地址,表示初始化调用的结束位置。
实现细节:
extern InitCall _zinitcall_sys##name##_end;:同样声明了一个外部变量,名称动态生成,指向初始化结束的地址。
InitCall *initCall = &_zinitcall_sys##name##_end;:将此结束变量的地址赋值给指针变量 initCall。
return (initCall);:宏最终返回这个指针。
这段代码通过定义 SYS_BEGIN 和 SYS_END 两个宏,提供了一种机制来获取系统初始化调用链的起始和结束地址。这在嵌入式系统或者需要高效启动过程的系统中非常重要。使用这些宏可以方便地管理和调用一系列初始化函数,以确保系统在启动过程中按照预定顺序正确地初始化各个组件。这种设计有助于提高代码的可读性和可维护性,使得添加或移除初始化函数更加简单和直观。
为什么指针能够指向函数开始与结束的地址:
在C语言中,函数和数组的名称都可以被视为常量指针,这意味着它们可以被用作指向它们在内存中起始地址的指针。这种特性使得我们能够创建指向函数的指针,特别是在嵌入式系统和需要进行初始化调用的场景中。
SYS_BEGIN 和 SYS_END 宏的实现,以下是具体解释:
函数的存储:在编译过程中,函数的代码被加载到内存中,编译器会为每个函数分配一个特定的内存地址。该地址即为函数的起始地址。因此,函数的名称能够直接用作指针,指向它的起始位置。
全局变量:在 SYS_BEGIN 和 SYS_END 宏中,通过 extern 声明的全局变量 _zinitcall_sys##name##_start 和 _zinitcall_sys##name##_end 实际上是用来标识一段特定的内存区域——这段区域用于存放函数指针,通常这些指针指向具体的初始化函数。这些变量的名称采用了特定的命名约定,以便在链接阶段能够找到该段内存的开始和结束位置。
指针赋值:在宏中,将全局变量的地址赋值给 initCall 指针,例如:
InitCall *initCall = &_zinitcall_sys##name##_start;
DiffCopyInsert
这表示 initCall 现在指向了初始化调用链中第一个函数的起始地址。使用类似的方法,SYS_END 宏指向初始化调用链的结束地址。
迭代调用:使用这些指针,程序可以轻松地遍历和调用所有的初始化函数。通过 initCall 指针,我们能够以一种简洁的方式访问这段内存区域中的所有函数指针。
指针能够指向函数的开始与结束地址,归功于函数在内存中的地址特性,结合外部全局变量的定义,使得对一组初始化函数的管理和调用变得有效。这种操作在嵌入式系统中尤为重要,因为它要求高效地初始化和管理资源。